home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / rexx / imc / rexx-imc.5 / rexx.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-25  |  104.5 KB  |  2,423 lines

  1. /* The basic interpreter functions of REXX/imc    (C) Ian Collier 1992 */
  2.  
  3. #include<sys/stat.h>
  4. #include<sys/file.h>
  5. #include<sys/socket.h>
  6. #include<malloc.h>
  7. #include<memory.h>
  8. #include<string.h>
  9. #include<signal.h>
  10. #include<stdlib.h>
  11. #include<unistd.h>
  12. #include "functions.h"
  13. #include"globals.h"
  14. #ifdef STUFF_STACK
  15. #include<sys/termios.h>
  16. #endif
  17.  
  18. static char rxmath[maxvarname];/* where to find rxmathfn */
  19. static char rxque[maxvarname]; /* where to find rxque */
  20. static char address0[maxvarname]="UNIX"; /* The initial environment */
  21. static char address1[maxvarname]="UNIX"; /* Environment buffer 1 */
  22. static char address2[maxvarname]="UNIX"; /* Environment buffer 2 */
  23.        char *address=address1; /* The current environment */
  24. static char **oldsource=0;     /* Temporary store for current program */
  25. static program *oldprog=0;     /* while loading a new one */
  26. static int oldlines=0;         /* Old number of lines */
  27. static int oldstmts=0;         /* Old number of statements */
  28. static char *oldlabels;        /* Old label pointer */
  29. static char *args=cnull;       /* argument string given to main() */
  30. static char *arglist[2];       /* argument list to main program */
  31. static int arglens[2];         /* lengths for main argument list */
  32. static int tmpstack=0;         /* whether a temporary pstack item is present */
  33. static unsigned sigstacklen;   /* number of elements allocated to sgstack */
  34. static int haltline=0;         /* line number where halt occurred */
  35. static int error=1;            /* "real" error versus return code from EXIT */
  36. static char version[40];       /* REXX version string */
  37. static char *signalto;         /* name of label to go to */
  38. static int ippc;               /* statement number of INTERPRET */
  39. static interpreting=0;         /* =1 while tokenising INTERPRETed data */
  40. static char rxstackholder[128];/* to hold the output of "rxque" */
  41.  
  42. /* the following structure mirrors struct sockaddr, but has a longer name
  43.    field.  It is to contain the file name of the stack socket. */
  44. static struct {u_short af;char name[maxvarname];} rxsockname={AF_UNIX};
  45. static int rxsocklen;          /* the length of the above structure */
  46. static int rxstackproc=0;      /* the process number of "rxque" */
  47.  
  48. /* when extra data has been found on the end of a clause, the following
  49.    distinguishes between "unexpected ')' or ','" and "invalid data" */
  50. #define Edata (c==')'||c==','?Erpar:Exend) 
  51.  
  52. main(argc,argv)  /* Initialise the various REXX structures, load the */
  53. int argc;        /* program and interpret it.  Return the result to the */
  54. char *argv[];    /* environment. */
  55. {
  56.    int c,f,l;
  57.    int optionx=0;     /* set if "-x" option present */
  58.    int minus=0;       /* set if "-" present (take from stdin) */
  59.    int opt;           /* argument counter */
  60.    unsigned arglen;   /* amount of space allocated to arg string */
  61.    unsigned argtot=0; /* length of arg string so far */
  62.    char *answer;      /* result of executing the program */
  63.    int anslen;        /* length of that result */
  64.    char *input=0;     /* The source code from disk or wherever */
  65.    int ilen;          /* The length of the source code */
  66.    int pipefd[2];
  67.    char *rxstackname=getenv("RXSTACK");
  68.    char *rxpath=getenv("REXXIMC");
  69.    struct fileinfo *info; /* for initialising stdin, stdout, stderr */
  70.    char *basename;    /* basename of the program to execute */
  71.    char *tail;        /* file extension of the program */
  72.    extern char *month[]; /* from rxdate() in rxfn.c */
  73.    
  74. #ifdef DEBUG
  75.    malloc_debug(2);
  76. #endif
  77. #ifdef HAS_MALLOPT
  78.    mallopt(M_MXFAST,1024);
  79. #endif
  80.  
  81. /* construct version string (should be constant, but it's easier this way) */
  82.    sprintf(version,"REXX/imc-%s %s %d %s %d",VER,LEVEL,DAY,month[MONTH-1],YEAR+1900);
  83. /* Construct the REXX auxiliary file path names */
  84.    strcpy(rxque,rxpath?rxpath:REXXIMC);   /* use $REXXIMC if possible, */
  85.    l=strlen(rxque);                       /* otherwise the REXXIMC macro */
  86.    rxque[l++]='/';
  87.    strcpy(rxque+l,rxquename);          
  88.    if(access(rxque,X_OK)){                /* rxque does not exist. */
  89.       l=0;
  90.       if(strchr(argv[0],'/')){            /* Try some other directory */
  91.          strcpy(rxque,argv[0]);           /* for instance our path name */
  92.      basename=strrchr(rxque,'/')+1;
  93.      strcpy(basename,rxquename);
  94.      if(!access(rxque,X_OK))l=basename-rxque;
  95.       }
  96.       if(!l){                             /* OK, now try the entire path! */
  97.          if(!which(rxquename,1,rxque)){
  98.         fprintf(stderr,"Unable to find \'%s\'\n",rxquename);
  99.         die(Einit);
  100.      }
  101.      l=strrchr(rxque,'/')+1-rxque;
  102.       }
  103.    }
  104.    memcpy(rxmath,rxque,l);                /* Assume the math file is in the */
  105.    strcpy(rxmath+l,rxmathname);           /* same place as rxque */
  106. /* open the stack */
  107.    if(!rxstackname){  /* it doesn't exist already, so fork off "rxque" */
  108.       if(pipe(pipefd))perror("pipe"),die(Einit);
  109.       if((f=vfork())<0)perror("vfork"),die(Einit);
  110.       if(!f){  /* the child: attach pipe to stdout and exec rxque */
  111.          if(dup2(pipefd[1],1)<0)perror("dup2"),_exit(-1);
  112.          close(pipefd[0]),close(pipefd[1]);
  113.          execl(rxque,"rxque",cnull);
  114.      perror(rxque);
  115.          _exit(-1);
  116.       } /* now the parent: read from pipe into rxstackholder. The answer
  117.             should be RXSTACK=(name) RXSTACKPROC=(number).  Split off the
  118.         second token, search for "=", store number in rxstackproc, and
  119.         put RXSTACK into the environment. */
  120.       close(pipefd[1]);
  121.       if(read(pipefd[0],rxstackholder,sizeof rxstackholder)<20
  122.        ||!(answer=strchr(rxstackholder,' '))
  123.        ||!(answer[0]=0,answer=strchr(answer+1,'='))
  124.        ||!(rxstackproc=atoi(answer+1)))
  125.          fputs("Cannot create stack process\n",stderr),die(Einit);
  126.       rxstackname=strchr(rxstackholder,'=')+1;
  127.       putenv(rxstackholder);
  128.       wait((int*)0);     /* delete child from process table */
  129.    }  /* The stack exists. Open a socket to it. */
  130.    strcpy(rxsockname.name,rxstackname),
  131.    rxsocklen=sizeof(u_short)+strlen(rxstackname);
  132.    if((rxstacksock=socket(AF_UNIX,SOCK_STREAM,0))<0)perror("socket"),die(Einit);
  133.    if(connect(rxstacksock,&rxsockname,rxsocklen)<0)perror("connect"),die(Einit);
  134. /* Argument processing */
  135.    /* Flags are all arguments starting with "-" until a "-x" or "-" found */
  136.    traceout=stderr;
  137.    for(opt=1;!optionx && !minus && opt<argc && argv[opt][0]=='-';opt++)
  138.       if(!setoption(argv[opt]+1,strlen(argv[opt]+1)))
  139.          switch(argv[opt][1]&0xdf){
  140.          case 'X':
  141.         optionx=1; break;
  142.      case 0:
  143.         minus=1; break;
  144.      case 'S':
  145.         if(++opt==argc)
  146.            errordata=": no program supplied",die(Einit);
  147.             input=allocm(ilen=1+strlen(argv[opt]));
  148.         memcpy(input,argv[opt],ilen);
  149.         input[ilen-1]='\n';
  150.         basename="<string>";
  151.         break;
  152.      case 'T':
  153.         if(!argv[opt][2])
  154.            if(++opt==argc)die(Etrace);
  155.            else settrace(argv[opt]);
  156.         else settrace(argv[opt]+2);
  157.         break;
  158.      case 'I':
  159.         input=allocm(32);
  160.         strcpy(input,"do forever;nop;end\n");
  161.         ilen=strlen(input);
  162.         basename="<trace>";
  163.         settrace("?a");
  164.         break;
  165.      default:
  166.         workptr=allocm(worklen=32+strlen(argv[opt]));
  167.             sprintf(workptr,": invalid option '%s'",argv[opt]);
  168.         errordata=workptr;
  169.         die(Einit);
  170.       }
  171.    if(opt==argc || input)
  172.       minus=1; /* minus==0 if and only if there is a program name */
  173.    /* get argument list in string form */
  174.    /* estimate length and get mem for arg list */
  175.    for(arglen=0,c=opt+!minus;c<argc;++c)arglen+=strlen(argv[c])+1;
  176.    args=allocm(arglen),
  177.    args[0]=0;
  178.    /* form list by concatenating all the arguments separated by spaces */
  179.    for(c=opt+!minus;c<argc;++c){
  180.       l=strlen(argv[c]);
  181.       memcpy(args+argtot,argv[c],l+1);
  182.       argtot+=l;
  183.       if(c<argc-1)
  184.          args[argtot++]=' ',
  185.          args[argtot]=0;
  186.    }
  187. /* make space for the REXX data structures and initialise them */
  188.    varstk=(int *)allocm(varstklen=256),
  189.    varstkptr=0,
  190.    varstk[0]=varstk[1]=0,
  191.    vartab=allocm(vartablen=1024);
  192.    for(l=0;l<3;l++)hashptr[l]=allocm(hashlen[l]=256);
  193.    cstackptr=allocm(cstacklen=256),
  194.    ecstackptr=0,
  195.    worklen=maxvarname+10,
  196.    workptr=allocm(worklen),
  197.    pstackptr=allocm(pstacklen=512),
  198.    sgstack=(struct sigstruct *)malloc(sizeof(struct sigstruct)*(sigstacklen=20)),
  199.    pull=allocm(pulllen=256),
  200.    varnamebuf=allocm(varnamelen=maxvarname);
  201. /* Get the program's details and load it */
  202.    if(input)optionx=0,strcpy(fname,"string");
  203.    else if(minus){
  204.       strcpy(fname,"<stdin>");
  205.       basename="<stdin>";
  206.       input=allocm(ilen=256);
  207.       l=0;
  208.       while(1){
  209.          l+=fread(input+l,1,256,stdin);
  210.      if(feof(stdin))break;
  211.      else mtest(input,ilen,l+256,256);
  212.       }
  213.       if(!l || input[l-1]!='\n')input[l++]='\n';
  214.       input=realloc(input,ilen=l);
  215.       if(ttyout=fopen("/dev/tty","w"))
  216.          fputs("  \b\b",ttyout),
  217.          fclose(ttyout);
  218.    }
  219.    else{
  220.       fname[0]=0;
  221.       if(basename=strrchr(argv[opt],'/'))basename++;
  222.       else basename=argv[opt];         /* basename points to the file's name */
  223.       if((tail=strrchr(basename,'.'))&&strlen(tail)<maxextension)
  224.          strcpy(extension,tail);       /* this will be the default extension */
  225.       else strcpy(extension,filetype); /* in this case use the system default*/
  226.       extlen=strlen(extension);
  227.       if(which(argv[opt],optionx,fname)!=1) /* search for the file */
  228.          errordata=fname,die(-3);      /* error - not found */
  229.       if(!(input=load(fname,&ilen)))
  230.          errordata=fname,die(-3);      /* Error - could not load file */
  231.    }
  232.    tokenise(input,ilen,0,optionx);
  233.    source[0]=allocm(strlen(fname)+1);
  234.    strcpy(source[0],fname);
  235. /* File initialisation: get some streams to access the tty, and set up the
  236.    REXX streams for stdin, stdout, stderr. */
  237.    if(!(ttyin=fopen("/dev/tty","r")))ttyin=stdin;
  238.    if(!(ttyout=fopen("/dev/tty","w")))ttyout=stderr;
  239.    (info=fileinit("stdin",cnull,stdin))->lastwr=0; /* set up stdin */
  240.    info->rdpos=info->wrpos;    /* wrpos has been set to the current position */
  241.    info->rdline=info->wrline;  /* now rdpos will be there as well */
  242.    fileinit("stdout",cnull,stdout)->wr=1; /* set up stdout and stderr */
  243.    fileinit("stderr",cnull,stderr)->wr=1; /* for writing */
  244. /* call the interpreter */
  245.    arglist[0]=args,                 /* there is one argument - "args" */
  246.    arglist[1]=cnull,
  247.    arglens[0]=argtot;
  248.    signal(SIGINT,halt_handler);
  249.    signal(SIGTERM,halt_handler);
  250.    signal(SIGHUP,halt_handler);
  251.    signal(SIGQUIT,sigtrace);
  252.    signal(SIGSEGV,error_handler);
  253.    signal(SIGBUS,error_handler);
  254.    signal(SIGILL,error_handler);
  255.    signal(SIGPIPE,error_handler);
  256.    interplev++;              /* now at level 0 of the interpreter */
  257.    answer=interpreter(&anslen,1,basename,"COMMAND",arglist,arglens,0,0);
  258.    interplev--; /* there are no longer any incarnations of the interpreter */
  259. /* Interpret the answer as a return code */
  260.    if(answer==cnull)die(0);  /* no answer, so assume 0 */
  261.    stack(answer,anslen);     /* use the calculator to convert the answer */
  262.    l=(getint(1));            /* to integer */
  263.    error=0;                  /* not an error but a return code. */
  264.    die(l);
  265.    /*NOTREACHED*/
  266. }
  267.  
  268. /* memory allocation. */
  269.  
  270. /* allocm(size) allocates "size" bytes of memory and returns the answer.
  271.    It dies if malloc returns an error. */
  272. /* mtest and dtest (macros, except during debug) check that the given REXX
  273.    structure is large enough; if not they try to extend it and die if realloc
  274.    fails.  dtest returns 1 if the area moved and sets mtest_diff to the
  275.    distance between the old and new pointers */
  276.    
  277. char *allocm(size)
  278. unsigned size;
  279. {  char *pointer;
  280.    if((pointer=malloc(size))==cnull)die(Emem);
  281. #ifdef DEBUG
  282.    /* tell what has been alloced */
  283.    printf("allocm: allocated (%lX,%d)\n",(long)pointer,size);
  284. #endif
  285.    return pointer;
  286. }
  287.  
  288. /* The non-debug version of mtest is now a macro; the debug version calls
  289.    this function. Note that in
  290.       mtest(memptr,alloc,length,extend)
  291.    memptr and alloc are identifiers. length is an expression which is
  292.    evaluated exactly once. extend is an expression which is evaluated
  293.    zero or one times. In all other ways, mtest acts like a function.
  294. */
  295. #ifdef DEBUG
  296. int mtest_debug(memptr,alloc,length,extend,diff)
  297. unsigned *alloc,length,extend;
  298. char **memptr;
  299. long *diff;
  300. {
  301.    static int elabptr=0;
  302.    static char **areas[]={&cstackptr,&pstackptr,&workptr,&vartab,&labelptr};
  303.    static char *aname[]={"cstack","pstack","worksp","variables","labels"};
  304.    static int  *lens[]={&cstacklen,&pstacklen,&worklen,&vartablen,&elabptr};
  305.    static int  num=5;
  306.    char *oldmemptr=*memptr;
  307.    int oldlen= *alloc;
  308.    int newlen= *alloc+extend;
  309.    int i,j;
  310.    char *a,*b,*c,*d;
  311.    static int doneit=0;
  312.    if((*alloc)>=length)return 0;
  313.     /* used to be if(doneit==2) */
  314.       doneit=1;
  315.       printf("Areas:\n");
  316.       for(j=0;j<num;j++)
  317.          printf(" %s (%lX,%d)\n",aname[j],(long)*areas[j],*lens[j]);
  318.    for(i=0;i<num&&*areas[i] !=oldmemptr;i++);
  319.    if((*memptr=realloc(*memptr,(*alloc)+=extend))==cnull)die(Emem);
  320.    printf("mtest: %s changed from (%lX,%d) to (%lX,%d)\n",i<num?aname[i]:"area",(long)oldmemptr,oldlen,(long)*memptr,newlen);
  321.    if(diff)*diff=*memptr-oldmemptr;
  322.    a=*memptr;
  323.    b=a+newlen;
  324.    if(!doneit)doneit=2;
  325.    for(j=0;j<num;j++){
  326.       if(j==i)continue;
  327.       c= *areas[j];
  328.       d=c+*lens[j];
  329.       if(!c)doneit=0;
  330.       if((c>=a&&c<=b)||(d>=a&&d<=b)||(a>=c&&a<=d))
  331.          printf("   overlaps with %s (%lX,%d)\n",aname[j],(long)*areas[j],*lens[j]);
  332.    }
  333.    return 1;
  334.    mallocmap();
  335. }
  336. #endif
  337.  
  338. void die(rc) /* Error (exception) handler: cleans up, prints message, and */
  339. int rc;      /* does all the usual things that happen at error time */
  340. {
  341.    int i=0,l;
  342.    int catch;   /* Whether the error is to be caught by SIGNAL ON xxx */
  343.    int lev;     /* nesting level of interpreter() to longjmp to if catch!=0 */
  344.    int bit;     /* bit to test against "trap" flags in signal structure */
  345.    int stmt;    /* Where to signal to if the error is caught */
  346.    char rcb[20];/* for printing the rc */
  347.    char *ptr;   /* for traversing the program stack */
  348.    char *edata; /* saved copy of errordata */
  349.    int errline; /* which line number to say the error occurred in */
  350.    int sigl;    /* What to set SIGL to */
  351. recurse:
  352.    edata=errordata;
  353.    if(prog)errline=sigl=prog[ppc].num;
  354.    errordata=cnull; /* Clear this now for next time; it has been saved */
  355.    if(rc==0)error=0; /* "end of program" is not an error. */
  356. /* find out whether the error is to be caught */
  357.    switch(rc){      /* find out what trap has occurred, based on rc */
  358.       case Ehalt:    bit=Ihalt;    break;
  359.       case Enovalue: bit=Inovalue; break;
  360.       case Eerror:   bit=Ierror;   break;
  361.       case Efailure: bit=Ifailure; break;
  362.       case Enotready:bit=Inotready;break;
  363.       case 0:
  364.       case Esys:
  365.       case Emem:     bit=0;        break;/* never catch `OK' or `out of memory'
  366.                                             or `failure in system service' */
  367.       default:       bit=Isyntax;
  368.    } /* Now check that bit against the signal stack */
  369.    catch=ppc>0 && interplev>=0 && (sgstack[interplev].bits&(1<<bit));
  370.                               /* catch>0 if signal was on or inherited */
  371.    if(catch){                 /* Find lev = level in which signal is caught */
  372.       for(lev=interplev;!(sgstack[lev].bitson&(1<<bit));lev--);
  373.       if(lev<=interact)catch=0; /* Do not trap errors in interactive command */
  374.    }
  375.    if(catch){                 /* Find lev = level in which signal is caught */
  376.       stmt=sgstack[lev].ppc[bit];
  377.       sgstack[lev].bits &= ~(1<<bit);  /* Turn off trapping immediately */
  378.       sgstack[lev].bitson &= ~(1<<bit);
  379.       sgstack[interplev].bits &= ~(1<<bit);
  380.       if(stmt<0){             /* If label not found, then see whether this
  381.                                  is to be caught.  If not then display an
  382.                  appropriate message.  (If it is then it
  383.                  will be caught later). */
  384.          catch=bit!=Isyntax&&(sgstack[lev].bits&(1<<Isyntax));
  385.      if(!catch){
  386.         ptr=pstack(20,sizeof(struct errorstack));/*Stack the trapped line*/
  387.         ((struct errorstack *)ptr)->prg=prog;   /* so that it appears in */
  388.         ((struct errorstack *)ptr)->stmts=stmts;/* the traceback         */
  389.         ppc=-stmt;
  390.         findsigl(&lev);               /* Find the SIGNAL ON instruction. */
  391.         if(bit==Inotready)
  392.            fprintf(traceout,"      +++ %s: %s",sigdata[bit],
  393.                              message(Eerrno+lasterror));
  394.             else fprintf(traceout,"      +++ %s",message(rc));
  395.         if(edata&&rc==Enovalue)fputs(" on ",traceout);
  396.         if(edata)fputs(edata,traceout);
  397.         putc('\n',traceout); /* This writes an informative message about */
  398.                              /* the trapped condition                    */
  399.             rc=Elabel;
  400.         goto recurse;      /* Report the label not found error.          */
  401.      }
  402.       }
  403.    }
  404.    else lev=0;           /* not caught: catch and lev are zero */
  405.    trcresult=0;          /* not nested inside scanning() */
  406.    if(prog && !ppc && rc>=0){  /* Error occurred while tokenising */
  407.       if(catch&&interpreting){ /* silently get rid of the interpreted text */
  408.      free(prog[0].source);
  409.      free(prog[0].line);
  410.      free((char*)prog);
  411.      prog=oldprog;
  412.      stmts=oldstmts;
  413.          errline=prog[ppc=ippc].num;
  414.      interpreting=0;
  415.       }
  416.       else{                    /* Noisily get rid of the new program */
  417.          ptr=prog[stmts].source;    /* The error occurred after this point */
  418.      if(!ptr)              /* default: start of the last line encountered*/
  419.         ptr=prog[stmts].source=source[lines],
  420.         prog[stmts].num=lines;
  421.      errline=prog[stmts].num;
  422.      /* Try and find a place to stop  */
  423.      for(i=64;i--&&ptr[0]!='\n'&&ptr[0];ptr++);
  424.          if(i<0)ptr[-1]=ptr[-2]=ptr[-3]='.';/* Elipsis if not at end of line */
  425.      prog[stmts++].sourcend=ptr;
  426.      i=pstacklev;
  427.      if(!interpreting)pstacklev=0;      /* Don't indent a program line */
  428.      else pstacklev++;                  /* do indent an interpret */
  429.      printstmt(stmts-1,0,1);
  430.      pstacklev=i;
  431.      if(interpreting){
  432.         free(prog[0].source);
  433.            free(prog[0].line);
  434.         free((char*)prog);
  435.         prog=oldprog;
  436.         stmts=oldstmts;
  437.         errline=prog[ppc=ippc].num;
  438.         interpreting=0;
  439.      }
  440.      else{
  441.         free(prog[0].line);
  442.         free((char*)prog);
  443.         prog=oldprog;
  444.         stmts=oldstmts;
  445.         free(source[0]);
  446.         free(source[1]);
  447.         free((char*)source);
  448.         free(labelptr);
  449.         lines=oldlines;
  450.         labelptr=oldlabels;
  451.         source=oldsource;
  452.         printf("Error %d running %s, line %d: %s",rc,fname,errline,message(rc));
  453.         if(edata)fputs(edata,stdout);
  454.         putchar('\n');
  455.         rc=Eincalled;
  456.         ppc=ippc;
  457.         if(totpstacklev){
  458.            totpstacklev--;
  459.            goto recurse; /* or: die(rc); */
  460.         }
  461.         else goto free;               /* This frees and exits */
  462.      }
  463.       }
  464.    }
  465. /* Get the name of the file in which the error occurred */
  466.    if (edata&&rc==-3)    /* the error data for rc -3 is a file name, */
  467.       strcpy(fname,edata);/* which will be printed by message() */
  468.    else if(source)       /* by default, use the current file name. If that */
  469.       strcpy(fname,source[0]);  /* doesn't exist, fname will already be OK */
  470. /* go down program stack and print out traceback */   
  471.    if (rc!=0&&error&&source){        /* source exists and it is a real error */
  472.       if(tmpstack)
  473.          tmpstack=0,delpstack();     /* remove temporary stack item */
  474.       if(!catch)printstmt(ppc,0,1);  /* print the line in error */
  475.       while(pstacklev){ 
  476.          i=unpstack();               /* find out what the next entry is */
  477.          if(i==13||i==16) /* stop when external call or interactive reached */
  478.         break;
  479.          if(catch)if(i==11||i==12)break; /* catch error in internal call */
  480.          freestack(delpstack(),i);       /* delete stack entry and clean up */
  481.          if(!catch)printstmt(newppc,0,1);/* print a traceback line */
  482.          if(!sigl &&interplev!=interact) /* if in INTERPRET (sigl==0) then */
  483.         sigl=prog[newppc].num;       /* point sigl to the INTERPRET */
  484.       }
  485.    }
  486. /* if(rc&&error&&!catch)printrc(rc);        the RC traceback line */
  487. /* finish off cleaning the program stack if the error was caught. */
  488.    while(catch&&pstacklev&&(!error||interplev!=interact)){
  489.       i=unpstack();  
  490.       if(i>=11&&i<=13&&interplev==lev) /* at CALL stack entry, and the level */
  491.          break;                        /* is right to catch the error */
  492.       freestack(delpstack(),i);
  493.    }
  494.    if(!errline)errline=sigl; /* don't say "error in line 0" if it happened
  495.                           during INTERPRET. Flag the INTERPRET instruction */
  496.    if(interact>=0&&interplev==interact&&error){
  497.       /* the error occurred while interpreting interactive data.  Print the
  498.          message and jump back to interactive trace mode. */ 
  499.       fputs(message(rc),ttyout);
  500.       if(edata)fputs(edata,ttyout);
  501. /*    if(errline)fprintf(ttyout," at line %d in %s",errline,fname); */
  502.       fputc('\n',ttyout);
  503.       _longjmp(interactbuf,1);
  504.    }
  505.    if(catch&&error){ /* the error has been caught. jump to the right label */
  506.       sprintf(rcb,"%d",rc);           /* set the special variable rc */
  507.       if(bit==Isyntax||bit==Ihalt||bit==Inovalue)
  508.          varset("RC",2,rcb,strlen(rcb));
  509.       if(stmt<=0){    /* If "label not found" was caught, go and catch it */
  510.          rc=Elabel;
  511.      goto recurse;
  512.       }
  513.       sgstack[interplev].type=1;      /* Store the information for CONTITION */
  514.       sgstack[interplev].which=bit;
  515.       sgstack[interplev].data=sigdata[bit];
  516.       sigdata[bit]=0;
  517.       if(bit==Inovalue && edata)
  518.          strcpy(sgstack[interplev].data=allocm(strlen(edata)),edata);
  519.       if(bit==Isyntax){
  520.          l=edata?strlen(edata):0;
  521.      l+=strlen(ptr=message(rc));
  522.      strcpy(sgstack[interplev].data=allocm(l+1),ptr);
  523.      if(edata)strcat(sgstack[interplev].data,edata);
  524.       }
  525.       if(bit==Ihalt)sigl=haltline;    /* in the case of halt, use stored sigl*/
  526.       sprintf(rcb,"%d",sigl);         /* set the special variable sigl */
  527.       varset("SIGL",4,rcb,strlen(rcb));
  528.       ppc=stmt;
  529.       _longjmp(sgstack[interplev].jmp,1);
  530.    }
  531. /* Print the error message */
  532.    if (rc>0&&error){
  533.       if(ppc<0)fprintf(stderr,"Error %d interpreting arguments: ",rc);
  534.       else fprintf(stderr,"Error %d running %s, line %d: ",rc,fname,errline);
  535.       fputs(message(rc),stderr);
  536.       if(edata)fputs(edata,stderr);
  537.       putc('\n',stderr);
  538.    }
  539.    if (rc<0&&error){
  540.       fputs(message(rc),stderr);
  541.       if(rc==-3 && edata)putc(' ',stderr);
  542.       if(edata)fputs(edata,stderr);
  543.       putc('\n',stderr);
  544.    }
  545. /* Continue if necessary */
  546.    if(totpstacklev){/* This was a called routine. Signal error 50 down below */
  547.       if(rc!=Ehalt)rc=Eincalled;
  548.       i=unpstack();
  549.       freestack(delpstack(),i); /* Remove the current program */
  550.       ppc=newppc;               /* this should address the call instruction */
  551.       goto recurse; /* I would call die(rc), but I thought this was better...*/
  552.    }
  553. free:
  554. /* Free all allocated areas */
  555.    if (args) free(args);
  556.    if (source) free(source[0]),free(source[1]),free((char*)source);
  557.    if (prog) free(prog[0].line),free((char*)prog);
  558.    if (vartab) free(vartab);
  559.    if (labelptr) free(labelptr);
  560.    if (cstackptr) free(cstackptr);
  561.    if (workptr) free(workptr);
  562.    if (pull)free(pull);
  563.    if (sgstack)free((char *)sgstack);
  564.    if (varnamebuf)free(varnamebuf);
  565. /* Close down the terminal, the stack and all the hashed info */
  566. #ifdef STUFF_STACK
  567.    while(1){                              /* Stuff stacked data to keyboard */
  568.       if(error ||
  569.          write(rxstacksock,"G",1)<1 ||
  570.          read(rxstacksock,pull,7)<7 ||
  571.      !memcmp(pull,"FFFFFF",6)) break; /* error or no more stacked data */
  572.       sscanf(pull,"%x",&l);
  573.       while(l--&&
  574.             read(rxstacksock,pull,1) &&
  575.             0==ioctl(fileno(ttyin),TIOCSTI,pull)); /* Stuff one character */
  576.       if(l>=0)break;
  577.       pull[0]='\n';                       /* a return at the end of each line*/
  578.       if(ioctl(fileno(ttyin),TIOCSTI,pull)) break;
  579.    }
  580. #endif
  581.    fclose(ttyin),fclose(ttyout);
  582.    if(rxstackproc)kill(rxstackproc,SIGTERM);
  583.    hashfree(); /* free hash tables. Note: this also closes stdout etc, so
  584.                   it is the last thing called before exit */
  585.    exit(rc);
  586. }
  587.  
  588. static char *interpreter(anslen,start,callname,howcall,args,arglen,inherit,delay)
  589. /* Interprets a program, or part of a program. Called by main() and the REXX
  590.    instructions which cause control to move temporarily.
  591.    The return value is NULL, or the address of a string, determined by what
  592.    is specified on EXIT or RETURN from the rexx program.  The length of the
  593.    result (if any) is stored in anslen.  */
  594.    
  595. int start;        /* which statement to start at */
  596. char *callname;   /* fourth token of "parse source" */
  597. char *howcall;    /* second token of "parse source" */
  598. char *args[];     /* array of arguments, ending with null pointer */
  599. int arglen[];     /* array of argument lengths */
  600. int *anslen;      /* length of the answer */
  601. int inherit;      /* Whether to inherit signals */
  602. int delay;        /* Whether to delay any signals */
  603. {
  604.    char *lineptr; /* Pointer to the current program line */
  605.    char *tmpptr;
  606.    int tmpchr;
  607.    char c,c2;
  608.    int len;
  609.    int i,m,e,z;
  610.    int up;        /* whether to uppercase (during PARSE) */
  611.    char *exp;
  612.    int l;
  613.    int chkend;    /* whether to check for a line terminator */
  614.    char varname[maxvarname];
  615.    int varlen;
  616.    char *varref;
  617.    int reflen;
  618.    char *parselist[maxargs+1]; /* list of strings to PARSE */
  619.    int parselen[maxargs+1];    /* lengths of those strings */
  620.    char psource[200];          /* the string parsed by PARSE SOURCE */
  621.    int stype,sllen,sslen;      /* used for DO and END */
  622.    char *slimit,*sstep,*svar;
  623.    int ilimit,istep,istart;
  624.    int whilep,untilp;          /* values of WHILE and UNTIL conditions */
  625.    char *entry;                /* address of a program stack entry */
  626.    char *mtest_old;
  627.    long mtest_diff;
  628.    int fr;                     /* number following FOR in a DO instruction */
  629.    int s;
  630.    int *lptr;
  631.    struct fileinfo *info;
  632.    long filepos;
  633.  
  634.    ppc=start;
  635.    /* set the string for PARSE SOURCE */
  636.    sprintf(psource,"UNIX %s %s %s %s",howcall,source[0],callname,address0);
  637.    
  638. /* save stack details in case of signal or signal on or exit. The return
  639.    from _setjmp is: 0 when called initially, 1 when jumped to after an error
  640.    is trapped, 2 during SIGNAL (when the stack is cleared) and
  641.    -1 when jumped to on EXIT */
  642.    if(inherit){
  643.       sgstack[interplev].bits=sgstack[interplev-1].bits,
  644.       sgstack[interplev].callon=sgstack[interplev-1].callon,
  645.       sgstack[interplev].delay=sgstack[interplev-1].delay|(1<<delay)&~1;
  646.       sgstack[interplev].type=sgstack[interplev-1].type;
  647.       sgstack[interplev].which=sgstack[interplev-1].which;
  648.       for(l=0;l<Imax;l++)sgstack[interplev].ppc[l]=sgstack[interplev-1].ppc[l];
  649.    }
  650.    else sgstack[interplev].bits=0,
  651.         sgstack[interplev].callon=0,
  652.     sgstack[interplev].delay=0,
  653.     sgstack[interplev].type=0,
  654.     sgstack[interplev].data=0;
  655.    sgstack[interplev].bitson=0;
  656.    sgstack[interplev].data=0;
  657.    if(delay){
  658.       sgstack[interplev].which=delay;
  659.       sgstack[interplev].type=2;
  660.       sgstack[interplev].data=sigdata[delay];
  661.       sigdata[delay]=0;
  662.    }
  663.    if((s=_setjmp(sgstack[interplev].jmp))<0){
  664.       /* after EXIT, return from external call with the result */
  665.       if(!returnval)return cnull;
  666.       stack(returnval,returnlen);
  667.       free(returnfree);
  668.       return delete(anslen);
  669.    }
  670. /* save the arguments (done here in case of a "signal on") */   
  671.    curargs=args,
  672.    curarglen=arglen;
  673.    if(s==2)goto signal;
  674.    if(s==0&&trcflag&Tclauses)printstmt(ppc-1,1,0); /* Trace opening comments */
  675. /* Loop for each statement */
  676.    while(ppc<stmts){
  677.       lineptr=prog[ppc].line;
  678.       ecstackptr=0; /* clear the calculator stack */
  679.  
  680.       /* tracing - check for labels */
  681.       if(prog[ppc].num&&(trcflag&Tlabels)){
  682.          for(lptr=(int *)labelptr;(l= *lptr)&&!(ppc== *(lptr+1));lptr+=2+align(l+1)/four);
  683.          if(l){  /* there is a label here */
  684.             fprintf(traceout,"%5d *=* %s:\n",prog[ppc].num,(char*)(lptr+2));
  685.             interactive();
  686.          }
  687.       }
  688.  
  689.       chkend=1;                     /* do check for line terminator */
  690.  
  691.       /* trace clauses */
  692.       if(trcflag&Tclauses){
  693.          if((c= *lineptr)==END&&pstacklev)
  694.             unpstack(),         /* at an END, print out also the DO */
  695.             pstacklev--,
  696.         printstmt(ppc,0,0),
  697.             printstmt(newppc,0,0),
  698.             pstacklev++;
  699.      else printstmt(ppc,0,0);
  700.          interactive();
  701.       }
  702. /* Select what to do on the first character of the line */
  703.       if(*lineptr<0)  /* i.e. a keyword */
  704.          switch(c2=*lineptr++){
  705.             case SAYN: /* If a parameter is given, print it on stdout. */
  706.             case SAY:  /* With SAY, follow it with a newline */
  707.                if(*lineptr){
  708.               tmpchr=0;
  709.                   exp=scanning(lineptr,&tmpchr,&len);
  710.           lineptr+=tmpchr;
  711.                   delete(&len);
  712.                   if(c2==SAY)exp[len++]='\n';
  713.                }else{
  714.                   if(c2==SAYN)break;
  715.                   len=1,
  716.                   exp="\n";
  717.                } /* mirror the charout function to print the data */
  718.                if(!(info=(struct fileinfo *)hashget(1,"stdout",&l)))break;
  719.                if(info->lastwr&&(filepos=ftell(info->fp))>=0&&filepos!=info->wrpos)
  720.                   info->wrpos=filepos,
  721.                   info->wrline=0;  /* position has been disturbed */
  722.                if(info->lastwr==0)fseek(info->fp,info->wrpos,0);
  723.                info->lastwr=1;
  724.                if(fwrite(exp,len,1,info->fp)){
  725.                   if(info->wrline)info->wrline++;
  726.                   info->wrchars=0;
  727.                   if((info->wrpos=ftell(info->fp))<0)info->wrpos=0;
  728.                }
  729.                else fseek(info->fp,info->wrpos,0);
  730.                if(c2==SAYN)fflush(info->fp);
  731.                break;
  732.             case DO: tmpstack=1,  /* stack the current position. */
  733.                entry=(char *)pstack(stype=0,sizeof(struct minstack));
  734.                if(!(c= *lineptr))               /* non-repetitive. */
  735.                   {tmpstack=0;break;}           /* do nothing. */
  736.                if(c>0) {     /* a repetition count or a variable follows */
  737.                   tmpchr=0;
  738.                   varref=lineptr;               /* save the var's reference */
  739.                   getvarname(lineptr,&tmpchr,varname,&varlen,maxvarname);
  740.                   reflen=tmpchr;
  741.                   if(lineptr[tmpchr]!='=') /* not a variable unless followed */
  742.                      varname[0]=0;         /* by "=" */
  743.                }
  744.                else varname[0]=0;   /* a keyword follows */
  745.                if(varname[0]){
  746. /* a variable clause was found. Begin by getting the start value, then get
  747.    the "TO", "BY" and "FOR" values. The values are stored as integer offsets
  748.    in the calculator stack, in case the stack moves. FOR is stored as an
  749.    integer value. */
  750.                   tmpchr++;           /* character after '=' */
  751.                   scanning(lineptr,&tmpchr,&len);
  752.           lineptr+=tmpchr;
  753.                   unplus(OPplus);   /* do "name = expri + 0" */
  754.                   istart=undelete(&len)-cstackptr;
  755.                   sllen= -1,  /* limit=default (=null) (length -1) */
  756.                   sslen= -1,  /* step=default (=1)   */
  757.                   fr= -1;     /* for=default (=null) */
  758.                   while((c=*lineptr)==TO||c==BY||c==FOR){
  759.              tmpchr=1;
  760.                      if(c==TO)
  761.                         slimit=scanning(lineptr,&tmpchr,&sllen),
  762.                         ilimit=slimit-cstackptr;
  763.                      else if(c==BY)
  764.                         sstep=scanning(lineptr,&tmpchr,&sslen),
  765.                         istep=sstep-cstackptr;
  766.                      else /* c==FOR */{
  767.                         scanning(lineptr,&tmpchr,&i);
  768.                         if((fr=getint(1))<0)die(Erange);
  769.                      }
  770.              lineptr+=tmpchr;
  771.                   }  /* a keyword or line terminator must follow: */
  772.                   if(c>0)die(Exdo);
  773.                      /* now stack the parameters in the correct order. This
  774.                         leaves unused copies further down the stack, but
  775.                         these are cleared at the end of the command anyway.
  776.                         Once stacked they will be copied into the program
  777.                         stack. */
  778.                   i=reflen+sllen+sslen+len+64+ecstackptr;
  779.                      /* make sure cstack doesn't move while stacking data */
  780.                   mtest(cstackptr,cstacklen,i,i-ecstackptr);
  781.                   stack(istart+cstackptr,len),
  782.                   varset(varname,varlen,istart+cstackptr,len),/* var = start */
  783.                   tmpchr=ecstackptr;  /* save the address of the following: */
  784.                   if(sllen>=0)stack(ilimit+cstackptr,sllen),sllen=1;
  785.                   else stack(cnull,sllen=0); /* sllen now is "limit given?" */
  786.                   if(sslen>=0)stack(istep+cstackptr,sslen);
  787.                   else stack("1",1);
  788.                   stack(varref,reflen+1), /* variable name plus the '=' */
  789.                   i=ecstackptr-tmpchr;/* i is the length of all that data */
  790.                   if dtest(pstackptr,pstacklen,i+30,i-pstacklen+30)
  791.                      entry+=mtest_diff; /* stack the data on the pstack */
  792.                   memcpy(entry+2*four,cstackptr+tmpchr,i),
  793.                   epstackptr+=i-2*four, /* now add the FOR num, the length, */
  794.                   (*(int *)(pstackptr+epstackptr))=fr,      /* and the type */
  795.                   (*(int *)(pstackptr+(epstackptr+=four)))=i+sizeof(struct forstack),
  796.                   (*(int*)(pstackptr+(epstackptr+=four)))=stype=10,
  797.                   epstackptr+=four,
  798. /* having constructed the program stack, make an initial test on the data */
  799.                   delete(&l);                       /* delete the varname */
  800.                   if(num(&m,&e,&z,&l)<0)die(Enum);  /* test the step      */
  801.                   delete(&l);                       /* delete the step    */
  802.                   if(sllen)binmin(4); /* if limit supplied, sub from value*/
  803.                   else stack("0",1);  /* else just stack 0                */
  804.                   if(!m)unmin(20);    /* Negate that if step >= 0         */
  805.                   num(&m,&e,&z,&l);   /* test the answer                  */
  806.                   if(m||!fr)          /* if that<0 or FOR==0 then leave   */
  807.                      {sllen=1,tmpstack=0;goto leaveit;}
  808.            }
  809. /* End of control variable processing; start of numeric count processing  */
  810.                else if(c>0){
  811.               tmpchr=0;
  812.                   scanning(lineptr,&tmpchr,&len);
  813.           lineptr+=tmpchr;
  814.                   if((i=getint(1))<0)die(Erange); /* i is the number */
  815.                   if(!i){ /* i==0 so leave already */
  816.                      sllen=1,
  817.                      tmpstack=0;
  818.                      goto leaveit;
  819.                   } /* Make a FOR stack containing the counter */
  820.                   ((struct forstack *)entry)->fornum=i,
  821.                   ((struct forstack *)entry)->len=sizeof(struct forstack),
  822.                   ((struct forstack *)entry)->type=stype=15,
  823.                   epstackptr+=four;
  824.                }
  825. /* Next deal with any other data (while/until/forever) */
  826.                /* first update the character pointer to the current position */
  827.                ((struct minstack *)entry)->pos=lineptr;
  828.                c=*lineptr;
  829.                if(c==FOREVER){ /* like UNTIL but no expression follows */
  830.                   if(!stype)((struct minstack *)entry)->type=8;
  831.                   c=*++lineptr;
  832.                }
  833.                if(c==WHILE||c==UNTIL){     /* s/if/while for multiple conds */
  834.                   if(!stype)               /* no control variable or counter */
  835.                      ((struct minstack *)entry)->type=stype=8;
  836.                   lineptr++;
  837.                   if(c==WHILE){           /* evaluate and test the value now */
  838.              tmpchr=0;
  839.                      scanning(lineptr,&tmpchr,&len);
  840.              lineptr+=tmpchr;
  841.                      if(num(&m,&e,&z,&l)<0)die(Enum);
  842.                      if(*lineptr>0)die(Exdo);
  843.                      if(z){sllen=1,tmpstack=0;goto leaveit;}
  844.                   } /* but jump past an UNTIL value. */
  845.                   else for(c=1;c&&c!=WHILE&&c!=UNTIL;c=*++lineptr);
  846.                }
  847. /* Finish off DO processing */
  848.                if(*lineptr)die(Exdo);    /* check for invalid data at end */
  849.                tmpstack=0;          /* stack entry is no longer temporary */
  850.                break;
  851.             case END:if(!pstacklev)die(Eend); /* no data on stack */
  852.            exp=pstackptr+epstackptr;      /* Get top stack entry */
  853.            stype= *((int *)exp-1);
  854.            exp-=  *((int *)exp-2);
  855.            newppc=((struct minstack *)exp)->stmt;
  856.            tmpptr=((struct minstack *)exp)->pos;
  857.                if(stype==2)                   /* top entry is SELECT */
  858.                   goto when;
  859.                if(stype>10&&stype!=15)die(Eend); /* top entry is not DO */
  860.                if(!stype){ /* a non-repetitive DO - just continue */
  861.                   delpstack();
  862.                   break;
  863.                }
  864. /* First the WHILE and UNTIL conditions are evaluated.  newppc and soff
  865.    point to them in the original DO instruction.  Variables whilep and untilp
  866.    will be set to true if the loop needs to be exited */
  867.                whilep=0;untilp=0;
  868.                c= *tmpptr;
  869.                if(c==FOREVER)c= *++tmpptr;               /* ignore FOREVER */
  870.                if(c==WHILE||c==UNTIL){    /* s/if/while for multiple conds */
  871.           tmpchr=1,
  872.                   scanning(tmpptr,&tmpchr,&len),
  873.           tmpptr+=tmpchr,
  874.                   eworkptr=0,
  875.                   len=num(&m,&e,&z,&l);
  876.                   /* so len>=0 if the number was valid, z=1 if it was zero. */
  877.                   if(len<0)die(Enum);
  878.                   if(c==UNTIL)untilp=!z; /* if untilp then break */
  879.                   else whilep=z;         /* if whilep then break */
  880.                }
  881. /*             if(c&&c!=-1&&c!=WHILE&&c!=UNTIL) / * if all the WHILEs and  */
  882.                                /* UNTILs were used up, then                */
  883.                                /* we should have reached the end of the DO.*/
  884.  /* OR */      if(c=*tmpptr) 
  885.                   delpstack(), /* If not, remove the DO's stack entry but  */
  886.                   ppc=newppc,  /* flag the error in the DO statement, not  */
  887.                                /* the END */
  888.                   die(Edata);
  889. /* Now, the UNTIL condition is tested before incrementing the control
  890.    variable (if any).  A symbol after the END, if any, must be skipped if
  891.    the loop is to be left at this point. */
  892.                if(untilp){
  893.                   if((c= *lineptr)==SELECT)die(Exend);
  894.                   if(c>0){
  895.                      if(stype!=10)die(Exend);   /* must be DO with control   */
  896.                      svar=pstackptr+epstackptr-3*four;/* point to FOR field  */
  897.                      svar-=four,                /* point to variable length  */
  898.                      svar -= align(varlen= *(int *)svar);/* point to name    */
  899.                      testvarname(&lineptr,svar,varlen-1);/* Check it matches */
  900.                   }
  901.                   delpstack();
  902.           /* Check for conditions before leaving */
  903.           tmpchr=ppc; ppc=newppc;doconds();ppc=tmpchr; 
  904.                   break;
  905.                }
  906. /* The integer counter (if any) is decremented, tested and added to the
  907.    WHILE condition */
  908.                if(stype==10||stype==15){
  909.                   svar=pstackptr+epstackptr-3*four;/* point to FOR field */
  910.                   if((fr= *(int *)svar)>0) /* get the FOR field */
  911.                      (*(int *)svar)= --fr; /* fr now holds its new value */
  912.                   if(!fr)whilep=1;
  913.                }
  914.                if(stype==10){
  915. /* the top stack entry is DO with variable. Increment the variable */
  916.                   svar-=four,              /* point to variable length */
  917.                   svar -= align(varlen= *(int *)svar); /* point to name */
  918.                   testvarname(&lineptr,svar,varlen-1); /* Check it matches */
  919.                   tmpchr=0;         /* Evaluate the symbol's current name */
  920.                   getvarname(svar,&tmpchr,varname,&varlen,maxvarname);
  921.                   if(!(exp=varget(varname,varlen,&len))) 
  922.                      die(Enum);     /* no value */ /* Er, what about NOVALUE?*/
  923.                   stack(exp,len),              /* stack the variable's value */
  924.                   sslen= *((int *)svar-1),     /* get the step's length */
  925.                   sstep=svar-align(sslen)-four,/* and the step's address */
  926.                   sllen= *((int *)sstep-1),    /* get the limit's length */
  927.                   slimit=sstep-align(sllen)-four,/* and the limit's value */
  928.                   stack(sstep,sslen),
  929.                   num(&m,&e,&z,&l),            /* get the step's sign in m */
  930.                   binplus(OPadd),              /* add step to value */
  931.                   tmpchr=ecstackptr,           /* get the result without */
  932.                   exp=delete(&len),            /* deleting it (by saving */
  933.                   ecstackptr=tmpchr,           /* ecstackptr) */
  934.                   varset(varname,varlen,exp,len); /* set the var's new value */
  935.                   if(sllen)                    /* if limit was given, */
  936.                      stack(slimit,sllen),      /* subtract it from value */
  937.                      binmin(4);
  938.                   else stack("0",1);           /* else just stack 0 */
  939.                   if(!m)unmin(20);             /* negate if step>=0 */
  940.                   num(&m,&e,&z,&l);            /* get sign in m */
  941.                   if(m||!fr)                   /* if loop has finished then */
  942.                      whilep=1;                 /* pretend the WHILE was false*/
  943.                } /* end if(DO with control variable) */
  944.                /* otherwise if END is followed by anything, it is an error. */
  945.                else if((c=*lineptr)==SELECT||c>0)die(Exend);
  946. /* So now leave if whilep is true, but iterate if it is false. */
  947.            tmpchr=ppc;ppc=newppc;
  948.            doconds();         /* Check for trapped conditions in the DO */
  949.                if(whilep)ppc=tmpchr,delpstack(); 
  950.                else lineptr=tmpptr;    /* copy the character ptr to the end */
  951.                                    /* of the DO clause */
  952.                break;
  953.             case IF: tmpchr=0,
  954.                scanning(lineptr,&tmpchr,&len);
  955.                if(num(&m,&e,&z,&l)<0)die(Enum); /* !z is the given value */
  956.                if(!(c=*lineptr))die(Edata);     /* line end reached      */
  957.            doconds();          /* trap conditions before continuing  */
  958.            if(ppc+1==stmts || prog[++ppc].line[0]!=THEN)die(Enothen);
  959.            if(++ppc==stmts)die(Eprogend);
  960.            chkend=0;           /* We will be already at start of a stmt */
  961.                if(!z)break;                    /* true: continue with THEN  */
  962.                skipstmt();                     /* false: skip THEN          */
  963.            if(prog[ppc].line[0]==ELSE)     /* if the next word is ELSE  */
  964.               if(++ppc==stmts)die(Eprogend);/* check for more statements*/
  965.           else break;                /* Do the stmt after the ELSE. */
  966.                                      /* Usually it would be skipped */
  967.                break;                 
  968.             case ELSE:chkend=0;    /* We will be already at start of a stmt */
  969.            if(++ppc==stmts)die(Eprogend);/* check for more statements   */
  970.            skipstmt();                   /* Skip the ELSE statement     */
  971.                break;
  972.             case SELECT:chkend=0;  /* We will be already at start of a stmt */
  973.                if(*lineptr)
  974.                   s=1, /* s means a value is given, and is on the stack */
  975.           tmpchr=0,
  976.                   scanning(lineptr,&tmpchr,&len),
  977.           lineptr+=tmpchr;
  978.                else s=0; /* it is a standard SELECT with no value */
  979.                if(c=*lineptr)die(Edata);
  980.                pstack(2,sizeof(struct minstack));/*stack SELECT entry */
  981.                if(++ppc==stmts)die(Eprogend);/* check for more statements   */
  982.            z=1;
  983.                while((lineptr=prog[ppc].line)[0]== WHEN){
  984.                   if(trcflag&Tclauses)
  985.              printstmt(ppc-1,1,0),
  986.                      printstmt(ppc,0,0);
  987.                   tmpchr=1;
  988.                   if(s)rxdup(); /* duplicate the SELECT value */
  989.                   scanning(lineptr,&tmpchr,&len); /* what comes after WHEN */
  990.           lineptr+=tmpchr;
  991.           if(c=*lineptr)die(Edata);
  992.           doconds();          /* trap conditions before continuing  */
  993.           if(1+ppc==stmts)die(Enothen);
  994.           if(prog[++ppc].line[0]!=THEN)die(Enothen);/* find a THEN  */
  995.           if(++ppc==stmts)die(Eprogend);/* check for more statements*/
  996.                   if(s)binrel(OPequ); /* Compare value with SELECT value */
  997.                   if(num(&m,&e,&z,&l)<0)die(Enum); /* test the result */
  998.                   delete(&l);
  999.                   if(!z)break;            /* True: follow this WHEN */
  1000.           if((c=prog[ppc].line[0])==WHEN||c==OTHERWISE)die(Ewhen);
  1001.                   skipstmt();
  1002.                }
  1003.            if(z){
  1004.               if((lineptr=prog[ppc].line)[0]!=OTHERWISE)
  1005.              die(Enowhen);      /* No correct alternative: error */
  1006.           if(++ppc==stmts)die(Eprogend);/* check for more statements*/
  1007.            }
  1008.                break;
  1009.             case OTHERWISE: /* for OTHERWISE and WHEN, just escape out of */
  1010.             case WHEN:      /* the current SELECT construction. */
  1011.                if((!pstacklev)||unpstack()!=2)
  1012.                   die(Ewhen); /* the WHEN wasn't inside a SELECT */
  1013.                when:
  1014.                while(prog[ppc].line[0]==WHEN){  /* find an END by repeatedly */
  1015.                   if(1+ppc==stmts)die(Enothen); /* skipping WHENs */
  1016.           if(prog[1+ppc].line[0]!=THEN)die(Enothen);
  1017.           if((ppc+=2)==stmts)die(Enoend);
  1018.           skipstmt();
  1019.            }
  1020.                if(prog[ppc].line[0]==OTHERWISE)/* and step over any OTHERWISE*/
  1021.                   findend();
  1022.                else if(prog[ppc].line[0]!=END)die(Enowhen);
  1023.                c=prog[ppc].line[1];         /* the character after END */
  1024.                if(c&&c!= SELECT)            /* must be SELECT or terminator */
  1025.                   die(Exend);
  1026.                epstackptr-=sizeof(struct minstack), /* delete stack entry */
  1027.                pstacklev--,totpstacklev--;
  1028.            lineptr=prog[ppc].line+1+(c!=0);
  1029.                chkend=1;                        /* do check for linend char */
  1030.                break;
  1031.             case OPTIONS: /* Split the option into tokens and call setoption */
  1032.            tmpchr=0,
  1033.            exp=scanning(lineptr,&tmpchr,&len),
  1034.            lineptr+=tmpchr;
  1035.                while(len){
  1036.               while(len&&*exp==' ')exp++,len--;
  1037.           if(!len)break;
  1038.               tmpptr=exp;
  1039.                   while(len&&*exp!=' ')exp++,len--;
  1040.           setoption(tmpptr,exp-tmpptr);
  1041.                }
  1042.                break;
  1043.             case PARSE: up=0;
  1044.                if(*lineptr == UPPER)lineptr++,up=1;/* up="upper case?" */
  1045.                i=1;                         /* one argument to parse usually */
  1046. /* Depending on the next keyword, copy the appropriate data into parselist[]
  1047.    and parselen[], setting i to the number of strings */
  1048.                switch(lineptr++[0]){
  1049.                   case ARG: for(i=0;args[i]!=cnull;i++){
  1050.                             parselist[i]=args[i];
  1051.                             if((parselen[i]=arglen[i])<0)parselen[i]=0;
  1052.                      }
  1053.                      break;
  1054.                   case SOURCE: parselist[0]=psource,
  1055.                      parselen[0]=strlen(psource);
  1056.                      break;
  1057.                   case PULL: /* first try the REXX data stack */
  1058.                      if(write(rxstacksock,"G",1)<1)die(Esys);
  1059.                      if(read(rxstacksock,pull,7)<7)die(Esys);
  1060.                      if(memcmp(pull,"FFFFFF",6)){
  1061.                         sscanf(pull,"%x",&l);
  1062.                         mtest(pull,pulllen,l,l-pulllen);
  1063.                         sllen=0;
  1064.                         while(sllen<l)
  1065.                            if((s=read(rxstacksock,pull,l))<1)die(Esys);
  1066.                            else sllen+=s;
  1067.                      }
  1068.                      else{  /* then try an input line */
  1069.                   case LINEIN: /* mirrors the linein() function */
  1070.                         if(!(info=(struct fileinfo *)hashget(1,"stdin",&l))){
  1071.                /* If it was closed by the user, signal on notready
  1072.                   or else just use an empty string */
  1073.                            rcset(Eeof,Enotready,"stdin");
  1074.                            l=0;
  1075.                         }else{
  1076.                            if(info->lastwr==0&&(filepos=ftell(info->fp))>=0&&filepos!=info->rdpos)
  1077.                               info->rdpos=filepos,
  1078.                               info->rdline=0; /* position has been disturbed */
  1079.                            clearerr(info->fp);
  1080.                            if(info->lastwr)fseek(info->fp,info->rdpos,0);
  1081.                            info->lastwr=0;
  1082.                c=sgstack[interplev].callon&(1<<Ihalt) |
  1083.                  sgstack[interplev].delay &(1<<Ihalt);
  1084.                            if(!c)siginterrupt(2,1);
  1085.                            l=0;
  1086.                            while((s=getc(info->fp))!=EOF&&s!='\n'){
  1087.                               mtest(pull,pulllen,l+1,256);
  1088.                               pull[l++]=s;
  1089.                }
  1090.                            siginterrupt(2,0);
  1091.                            if(delayed[Ihalt] && !c)
  1092.                               delayed[Ihalt]=0,
  1093.                               fseek(info->fp,info->rdpos,0),   /* reset to */
  1094.                               die(Ehalt);    /* start of line, if possible */
  1095.                            if(info->rdline)info->rdline++;
  1096.                            info->rdchars=0;
  1097.                            if(s==EOF&&!l)rxseterr(info,"stdin");
  1098.                            if((info->rdpos=ftell(info->fp))<0)info->rdpos=0;
  1099.                         }
  1100.                      }
  1101.                      parselist[0]=pull,
  1102.                      parselen[0]=l;
  1103.                      break;
  1104.                   case VALUE: i=0;
  1105.                      while(1){
  1106.                 tmpchr=0,
  1107.                         parselist[i]=scanning(lineptr,&tmpchr,&parselen[i]),
  1108.             lineptr+=tmpchr;
  1109.                         if((c= *lineptr)== WITH)break;
  1110.                         if(c!=','||i==maxargs)die(Eparse);
  1111.                         while(*lineptr==',')lineptr++,parselist[++i]="",
  1112.                            parselen[i]=0;
  1113.                      }
  1114.                      i++,
  1115.                      lineptr++;
  1116.                      break;
  1117.                   case VAR: tmpchr=0,
  1118.              getvarname(lineptr,&tmpchr,varname,&varlen,maxvarname);
  1119.              lineptr+=tmpchr;
  1120.                      if(varname[0]==0)die(Enosymbol);
  1121.                      if((exp=varget(varname,varlen,&parselen[0]))==cnull){
  1122.                         if((varname[0]&128)&&!memchr(varname,'.',varlen))
  1123.                            varname[varlen++]='.'; /* Add dot to a stem */
  1124.                         varname[0]&=127;
  1125.             varname[varlen]=0;
  1126.                         if(sgstack[interplev].bits&(1<<Inovalue))
  1127.                errordata=varname,
  1128.                die(Enovalue);   /* A novalue error was caught */
  1129.                         parselist[0]=varname,
  1130.                         parselen[0]=strlen(varname);
  1131.                      }
  1132.                      else{/* Copy the variable's value to pull.  We can't
  1133.                      use the value pointer itself because that might
  1134.                  move while the template is being interpreted */
  1135.                 mtest(pull,pulllen,parselen[0],parselen[0]-pulllen);
  1136.             memcpy(parselist[0]=pull,exp,parselen[0]);
  1137.              }
  1138.                      break;
  1139.                   case VERSION: parselist[0]=version,
  1140.                      parselen[0]=strlen(version);
  1141.                      break;
  1142.                   case NUMERIC: /* Make details [len(pull)>25] */
  1143.                      sprintf(pull,"%d %d %s",precision,fuzz-precision,
  1144.                         numform?"ENGINEERING":"SCIENTIFIC");
  1145.                      parselist[0]=pull,
  1146.                      parselen[0]=strlen(pull);
  1147.                      break;
  1148.                   default: die(Eform); /* an invalid subkeyword was found */
  1149.                }
  1150.                parselist[i]=cnull;     /* terminate the list */
  1151. /* Now would be a good time to uppercase, I think... */
  1152.                if(*lineptr)                   /* if a template supplied, */
  1153.               tmpchr=0,
  1154.                   parse(parselist,parselen,up,lineptr,&tmpchr),
  1155.           lineptr+=tmpchr;
  1156.                break;
  1157.             case EXIT: /* Get the value if any and jump back to the outermost
  1158.                        level of interpretation in the current program. */
  1159.                if(*lineptr){
  1160.               tmpchr=0;
  1161.                   returnval=scanning(lineptr,&tmpchr,&returnlen);
  1162.                   if(c=lineptr[tmpchr])die(Edata);
  1163.                   returnfree=cstackptr;  /* this way the result doesn't get */
  1164.                   cstackptr=allocm(cstacklen=returnlen+16);
  1165.                                          /* destroyed if the calc stack is  */
  1166.                                          /* freed by the following code     */
  1167.                }
  1168.                else returnval=0;
  1169.                while(pstacklev){
  1170.               stype=unpstack();
  1171.                   /* delete every program stack entry until an external call */
  1172.                   if(!prog[ppc].num) /* if an error occurs during INTERPRET, */
  1173.                      ppc=newppc;               /* blame the INTERPRET instr. */
  1174.                   freestack(delpstack(),stype);
  1175.                }
  1176.                _longjmp(sgstack[interplev].jmp,-1);
  1177.             case RETURN: /* Just return, with the given value if any */
  1178.                if(*lineptr){
  1179.               tmpchr=0;
  1180.                   scanning(lineptr,&tmpchr,&len);
  1181.           if(c=lineptr[tmpchr])die(Edata);
  1182.                   return delete(anslen);
  1183.                }
  1184.                return anslen[0]=0,cnull;
  1185.             case CALL:
  1186.            if((c= *lineptr)==ON||c==OFF){ /* set or clear a trap */
  1187.               findsigl(&istart);            /* find the start level */
  1188.           prog=oldprog,stmts=oldstmts;  /* number to affect */
  1189.                   i=gettrap(&lineptr,c==ON,&l); /* Get the trap name */
  1190.           if(c==ON){
  1191.              if(!l)
  1192.                 if(prog[ppc].num)l=-ppc;
  1193.             else
  1194.                    sprintf(workptr,": \'%s\'",varnamebuf),
  1195.                errordata=workptr,
  1196.                die(Elabel);
  1197.              for(e=istart;e<=interplev;e++)
  1198.                   sgstack[e].bits   &=~(1<<i),
  1199.                 sgstack[e].bitson &=~(1<<i),
  1200.                 sgstack[e].delay  &=~(1<<i),
  1201.                 sgstack[e].callon |= (1<<i),
  1202.             sgstack[e].ppc[i]=l;
  1203.           }
  1204.           else for(l=istart;l<=interplev;l++)
  1205.              sgstack[l].bits   &=~(1<<i),
  1206.              sgstack[l].bitson &=~(1<<i),
  1207.              sgstack[l].delay  &=~(1<<i),
  1208.              sgstack[l].callon &=~(1<<i);
  1209.               break;
  1210.            }
  1211.            tmpchr=0,          /* get details, then call rxcall() */
  1212.                z=gettoken(lineptr,&tmpchr,varname,maxvarname,0)-1;
  1213.            lineptr+=tmpchr;
  1214.                /* so varname holds the routine name, z=0 if it wasn't quoted */
  1215.                i=m=0;             /* i=arg count; m=last character was comma */
  1216.                if(*lineptr==' ')
  1217.                   lineptr++;                  /* A space may follow the name */
  1218.                while(c= *lineptr){                 /* now loop for arguments */
  1219.                   if(c==',')stacknull();
  1220.                   else tmpchr=0,scanning(lineptr,&tmpchr,&len),lineptr+=tmpchr;
  1221.                   i++;
  1222.                   if(m=(*lineptr==','))lineptr++;
  1223.                }
  1224.                if(m)stacknull(),i++;
  1225.            doconds();            /* Before calling, check for conditions */
  1226.                if(rxcall(0,varname,i,z,"SUBROUTINE")) /* call it */
  1227.                   exp=delete(&len),     /* a result was given, so set RESULT */
  1228.                   varset("RESULT",6,exp,len);
  1229.                else varset("RESULT",6,cnull,-1); /* no result, so drop RESULT*/
  1230.                timeflag&= (~2); /* in case of "call time" don't make a lasting
  1231.                                    timestamp */
  1232.                break;
  1233.             case SIGNAL:
  1234.            /* go down stack to find l=most recent nonzero line no */
  1235.            l=findsigl(&istart);
  1236.            prog=oldprog,stmts=oldstmts;
  1237.                if((c= *lineptr)==ON||c==OFF){   /* set or clear a trap */
  1238.               i=gettrap(&lineptr,c==ON,&l); /* Get the trap name */
  1239.           if(c==ON){
  1240.              if(!l)
  1241.                  if(prog[ppc].num)l=-ppc; /* flag the stmt in error */
  1242.                 else
  1243.                sprintf(workptr,": \'%s\'",varnamebuf),
  1244.                errordata=workptr,
  1245.                die(Elabel);        /* die if we are interpreted*/
  1246.              sgstack[istart].ppc[i]=l;
  1247.              sgstack[istart].bitson |=(1<<i);
  1248.              for(l=istart;l<=interplev;l++)
  1249.                 sgstack[l].bits   |= (1<<i),
  1250.             sgstack[l].callon &=~(1<<i),
  1251.             sgstack[l].delay  &=~(1<<i);
  1252.           }
  1253.                   else for(l=istart;l<=interplev;l++)
  1254.              sgstack[l].bits   &= ~(1<<i),
  1255.              sgstack[l].bitson &= ~(1<<i),
  1256.              sgstack[l].callon &= ~(1<<i),
  1257.              sgstack[l].delay  &= ~(1<<i);
  1258.                   break;
  1259.                } /* else signal to a given label name. Get the name, set the
  1260.                     source line number and clear the machine stack first */
  1261.            tmpchr=0;
  1262.                gettoken(lineptr,&tmpchr,varname,maxvarname,1);
  1263.                signalto=varname;
  1264.                if(lineptr[tmpchr])die(Edata);
  1265.                doconds();            /* Before going, check for conditions */
  1266.                ppc=l;
  1267.                if(istart!=interplev) /* Clear the stack if necessary */
  1268.               _longjmp(sgstack[istart].jmp,2);
  1269.                /* Code to transfer control to a label starts here */
  1270. signal:        while(pstacklev&&((stype=unpstack())<11||stype>13))
  1271.                   freestack(delpstack(),stype);
  1272.                   /* quit all current DO, SELECT, INTERPRET constructs */
  1273.                for(lptr=(int *)labelptr;
  1274.                   (l= *lptr)&&strcasecmp(signalto,(char *)(lptr+2));
  1275.                   lptr+=2+align(l+1)/four);
  1276.                if(!l) /* the label wasn't found */
  1277.                   sprintf(workptr,": \'%s\'",signalto),
  1278.                   errordata=workptr,
  1279.                   die(Elabel);  
  1280.                /* before jumping, save current ppc in variable SIGL */
  1281.                sprintf(varname,"%d",prog[ppc].num),
  1282.                varset("SIGL",4,varname,strlen(varname)),
  1283.                ppc=lptr[1],
  1284.                chkend=0;
  1285.                break;
  1286.             case ITERATE: /* Find the END and jump to it */
  1287.                tmpchr=epstackptr,
  1288.                istart=pstacklev,
  1289.            ilimit=totpstacklev,
  1290.                sllen=1;
  1291.                if (c= *lineptr){
  1292.                   if(rexxsymbol(c)<1)die(Enosymbol);
  1293.                   varref=lineptr;
  1294.                   reflen=0;
  1295.                   skipvarname(lineptr,&reflen);
  1296.           if(c=lineptr[reflen])die(Edata);
  1297.                }
  1298.                else {
  1299.               reflen=0;
  1300.           if(*lineptr)
  1301.              die(Enosymbol);/* symbol expected; we got something else*/
  1302.            }
  1303.                /* so (varref,reflen) is a control variable or a null string */
  1304.                while(1){ /* delete stack items until the right loop found. The
  1305.                          number of ENDs needed is counted in sllen */
  1306.                   while(pstacklev&&(stype=unpstack())<8) /* not a loop */
  1307.                      delpstack(),sllen++;
  1308.                   if(!pstacklev||stype>10&&stype!=15) /* function call */
  1309.                      epstackptr=tmpchr,pstacklev=istart,totpstacklev=ilimit,
  1310.                      die(Eleave); /* so the required loop is not active */
  1311.                   if(stype==8||stype==15) /* un-named DO loop */
  1312.                      if(!reflen)break;    /* OK if no name found */
  1313.                      else {delpstack(),sllen++;continue;}
  1314.                   /* otherwise the top stack entry is a DO with variable */
  1315.                   svar=pstackptr+epstackptr-4*four,
  1316.                   svar -= align(len= *(int *)svar); /* point to the name */
  1317.                   if(!(reflen&&(len-1!=reflen||memcmp(varref,svar,reflen))))
  1318.                      break; /* the correct DO loop has been found */
  1319.                   sllen++,delpstack();
  1320.                }
  1321.                stype= *((int *)(pstackptr+epstackptr)-1); /* the type of loop
  1322.                                                              being iterated */
  1323.                while(sllen--){ /* find the right number of ENDs */
  1324.                   findend();
  1325.                   if(sllen)
  1326.              if(++ppc==stmts)die(Enoend);
  1327.                }
  1328.                /* now test the name following the END */
  1329.                if(stype==10){
  1330.                   svar=pstackptr+epstackptr-4*four,
  1331.                   svar -= align(len= *(int *)svar);
  1332.           lineptr=prog[ppc].line+1;
  1333.                   testvarname(&lineptr,svar,len-1);
  1334.                }
  1335.                else if (c=prog[ppc].line[1])die(Edata);
  1336.                chkend=0;      /* Already at the start of a statement */
  1337.                break;
  1338.             case LEAVE: /* LEAVE is essentially the same as ITERATE, but it
  1339.                         goes past the END after finding it */
  1340.                tmpchr=epstackptr,
  1341.                istart=pstacklev,
  1342.                sllen=1;
  1343.                if (c= *lineptr){
  1344.                   if(rexxsymbol(c)<1)die(Enosymbol);
  1345.                   varref=lineptr;
  1346.                   reflen=0;
  1347.                   skipvarname(lineptr,&reflen);
  1348.                   if(c=lineptr[reflen])die(Edata);
  1349.                }
  1350.                else {
  1351.               reflen=0;
  1352.           if(*lineptr)die(Enosymbol);
  1353.            }
  1354.                while(1){
  1355.                   while(pstacklev&&((stype=unpstack())<8))
  1356.                      delpstack(),sllen++;
  1357.                   if(!pstacklev||stype>10&&stype!=15)
  1358.                      epstackptr=tmpchr,pstacklev=istart,totpstacklev=ilimit,
  1359.              die(Eleave);
  1360.                   if(stype==8||stype==15)
  1361.                      if(!reflen)break;
  1362.                      else {delpstack(),sllen++;continue;}
  1363.                   svar=pstackptr+epstackptr-4*four,
  1364.                   svar -= align(len= *(int *)svar);
  1365.                   if(!(reflen&&(len-1!=reflen||memcmp(varref,svar,reflen))))
  1366.                      break;
  1367.                   sllen++,delpstack();
  1368.                }
  1369.             leaveit: /* find the "sllen"th END and jump past it */
  1370.            if(ppc+1==stmts)die(Enoend); /* Get past the LEAVE or, more */
  1371.            ppc++;                       /* importantly, the DO */
  1372.                stype= *((int *)(pstackptr+epstackptr)-1);
  1373.                while(sllen--){
  1374.                   findend();
  1375.                   if(sllen)
  1376.              if(++ppc==stmts)die(Enoend);
  1377.                }
  1378.            lineptr=prog[ppc].line+1;
  1379.                if(stype==10){ /* test the name given after END */
  1380.                   svar=pstackptr+epstackptr-4*four,
  1381.                   svar -= align(len= *(int *)svar);
  1382.                   testvarname(&lineptr,svar,len-1);
  1383.                }
  1384.                else if (c= *lineptr)die(Edata);
  1385.                delpstack(); /* delete stack entry and continue past the END */
  1386.             case NOP: break;/* do nothing, like it says... */
  1387.             case INTERPRET: /* Get the details and call rxinterp */
  1388.            tmpchr=0;
  1389.                exp=scanning(lineptr,&tmpchr,&len);
  1390.            lineptr+=tmpchr;
  1391.                if(trcflag&Tclauses){ /* trace the interpret data */
  1392.                   if(prog[ppc].num)fprintf(traceout,"%5d *~* ",prog[ppc].num);
  1393.                     else fputs("      *~* ",traceout);
  1394.                   for(i=0;i<traceindent*pstacklev;i++)putc(' ',traceout);
  1395.                   for(i=0;i<len;i++)putc(exp[i],traceout);
  1396.                   putc('\n',traceout);
  1397.                }
  1398.                exp=rxinterp(exp,len,anslen,callname,howcall,args,arglen);
  1399.            if(*anslen>=0)
  1400.                   return exp; /* "interpret 'return x'" causes x to be returned
  1401.                               from rxinterp.  Convey it back to the caller */
  1402.                break;
  1403.             case PROCEDURE: /* Make a new variable table, then examine the
  1404.                             instruction and copy or hide variables */
  1405.                if(*((int *)(pstackptr+epstackptr)-1)!=11)
  1406.                   die(Eprocedure);    /* not in an internal call */
  1407.                (*((int *)(pstackptr+epstackptr)-1))++;
  1408.                   /* signal that PROCEDURE has been done */
  1409.                newlevel(); /* Make a complete new level of variables */
  1410.                if (!(c= *lineptr))
  1411.                   break;   /* OK if no further data follows */
  1412.                lineptr++;
  1413.                i=1; /* i.e. start of data */
  1414.                if(c==EXPOSE){ /* Expose all the given variables with varcopy */
  1415.                   while(i||(c= *lineptr)==' '||c=='('){
  1416.                      if(!i&&c!='(')lineptr++; /* step over the space */
  1417.                      i=0;
  1418.                      if((c=*lineptr)=='(')lineptr++;
  1419.              tmpchr=0;
  1420.                      getvarname(lineptr,&tmpchr,varname,&varlen,maxvarname);
  1421.              lineptr+=tmpchr;
  1422.                      if(!varname[0])die(Enosymbol);
  1423.                      varcopy(varname,varlen);
  1424.                      if(c=='('){             /* Expose a list of variables */
  1425.                         if(lineptr++[0]!=')')die(Elpar);
  1426.                         if((c=*lineptr)&&c!=' ')
  1427.                                   /* space is not required, */
  1428.                            i=1;   /* but if omitted remember not to skip it */
  1429.                         exp=varget(varname,varlen,&len);
  1430.                         tmpchr=0; /* prepare to parse the list of symbols */
  1431.                         if(exp&&len>0){
  1432.                            mtest(workptr,worklen,len+1,len-worklen+1);
  1433.                            for(c=0,l=0;l<len;l++){ /* copy the list in uc */
  1434.                               if(!c&&((c2=exp[l])=='\''||c2=='\"'))c=c2;
  1435.                               else if((c2=exp[l])==c)c=0; /* c is quote flag */
  1436.                               workptr[l]=c?c2:uc(c2); /* uppercase and copy */
  1437.                            }
  1438.                            if(c)die(Equote);
  1439.                            workptr[len]=0;          /* Now add a terminator */
  1440.                            while(l||workptr[tmpchr]==' '){
  1441.                               if(!l)tmpchr++; /* step over the space */
  1442.                               l=0;
  1443.                               getvarname(workptr,&tmpchr,varname,&varlen,maxvarname);
  1444.                               if(!varname[0])die(Enosymbol);
  1445.                               varcopy(varname,varlen);
  1446.                            } /* should now have reached the end of the list */
  1447.                            if(tmpchr!=len)die(Enosymbol);
  1448.                         }
  1449.                      }
  1450.                   }
  1451.                }
  1452.                else if(c!= HIDE)die(Eform); /* invalid subkeyword */
  1453.                else { /* Copy the entire variable table, then delete the */
  1454.                   vardup(); /* named variables with vardel */
  1455.                   while(i||(c= *lineptr)==' '){
  1456.                      if(!i)lineptr++;
  1457.                      i=0;
  1458.              tmpchr=0;
  1459.                      getvarname(lineptr,&tmpchr,varname,&varlen,maxvarname);
  1460.              lineptr+=tmpchr;
  1461.                      if(!varname[0])die(Enosymbol);
  1462.                      vardel(varname,varlen);
  1463.                   }
  1464.                }
  1465.             break;
  1466.             case NUMERIC: /* get parameter, and set global variable */
  1467.            tmpchr=0;
  1468.                if((c=lineptr++[0])==FORM){
  1469.                   gettoken(lineptr,&tmpchr,varname,maxvarname,1);
  1470.           lineptr+=tmpchr;
  1471.                   if(!strcmp(varname,"SCIENTIFIC"))numform=0;
  1472.                   else if(!strcmp(varname,"ENGINEERING"))numform=1;
  1473.                   else die(Eform); /* invalid subkeyword */
  1474.                   break;
  1475.                }
  1476.                if(c>0)die(Eform); /* a word must follow, not characters */
  1477.                if(c>=-1)die(Enosymbol); /* nothing followed */
  1478.                scanning(lineptr,&tmpchr,&len), /* an integer must follow */
  1479.            lineptr+=tmpchr;
  1480.                i=getint(1);
  1481.                if(i<0||i>maxdigits)die(Erange);
  1482.                if(c==DIGITS)
  1483.                   if(!i)die(Erange);
  1484.                   else precision=i,fuzz=i;
  1485.                else if(c== FUZZ){
  1486.                   if((i=precision-i)<1)die(Erange);
  1487.                   fuzz=i;
  1488.                }
  1489.                else die(Eform); /* invalid subkeyword */
  1490.                break;
  1491.             case THEN: /* can't have THEN in the middle of a program */
  1492.                die(Ethen);
  1493.             case TRACE: /* Get the data and set trcflag as appropriate */
  1494.            tmpchr=0;
  1495.            if(*lineptr)gettoken(lineptr,&tmpchr,varname,maxvarname,1),
  1496.            lineptr+=tmpchr;
  1497.            else varname[0]=0;
  1498.                if(!(trcflag&Tinteract)&&interact<0 ||
  1499.                (interact==interplev-1 && interact>=0)){
  1500.                      /* if interactive trace is on, do not
  1501.                      interpret any trace instruction except in the actual
  1502.                      command.  Moreover, use the saved trace flag as the
  1503.                      initial value of trcflag. This trace instruction makes
  1504.                      the program continue operating (trclp=0). */
  1505.                   if (interact>=0)trclp=0,trcflag=otrcflag;
  1506.                   settrace(varname);
  1507.                }
  1508.                break;
  1509.             case DROP: /* Go along the list, setting each variable to a null */
  1510.                i=1;    /* value (with length -1).  varset() does the DROP.   */
  1511.                while(i||(c=*lineptr)==' '||c=='('){
  1512.                   if(!i&&c!='(')lineptr++;
  1513.                   i=0;
  1514.                   if((c= *lineptr)=='(')lineptr++;
  1515.           tmpchr=0;
  1516.                   getvarname(lineptr,&tmpchr,varname,&varlen,maxvarname);
  1517.           lineptr+=tmpchr;
  1518.                   if(!varname[0])die(Enosymbol);
  1519.                   if(c=='('){  /* drop a list of variables */
  1520.                      if(lineptr++[0]!=')')die(Elpar);
  1521.                      if((c= *lineptr)&&c!=' ')
  1522.                                /* space is not required, */
  1523.                         i=1;   /* but if omitted remember not to skip it */
  1524.                      exp=varget(varname,varlen,&len);
  1525.                      tmpchr=0; /* prepare to parse the list of symbols */
  1526.                      if(exp&&len>0){
  1527.                         mtest(workptr,worklen,len+1,len-worklen+1);
  1528.                         for(c=0,l=0;l<len;l++){ /* copy the list in uc */
  1529.                            if(!c&&((c2=exp[l])=='\''||c2=='\"'))c=c2;
  1530.                            else if((c2=exp[l])==c)c=0; /* c is quote flag */
  1531.                            workptr[l]=c?c2:uc(c2); /* uppercase and copy */
  1532.                         }
  1533.                         if(c)die(Equote);
  1534.                         workptr[len]=0;          /* Now add a terminator */
  1535.                         while(l||workptr[tmpchr]==' '){
  1536.                            if(!l)tmpchr++; /* step over the space */
  1537.                            l=0;
  1538.                            getvarname(workptr,&tmpchr,varname,&varlen,maxvarname);
  1539.                            if(!varname[0])die(Enosymbol);
  1540.                            varset(varname,varlen,cnull,-1);
  1541.                         } /* should now have reached the end of the list */
  1542.                         if(tmpchr!=len)die(Enosymbol);
  1543.                      }                     
  1544.                   }/* don't remove the following "else" */
  1545.                   else varset(varname,varlen,cnull,-1);
  1546.                }
  1547.                break;
  1548.             case ADDRESS: /* Get parameter; perhaps follwed by a command */
  1549.                if(*lineptr){ /* Something follows... */
  1550.               tmpchr=0;
  1551.                   i=gettoken(lineptr,&tmpchr,varname,maxvarname,1);
  1552.           lineptr+=tmpchr;
  1553. /*                if(strcmp(varname,"UNIX")&&strcmp(varname,"COMMAND"))
  1554.                      die(Eform); / * This wouldn't usually happen... */
  1555.                }
  1556.                else i=-1;
  1557.                if(*lineptr==' ')
  1558.                   lineptr++;   /* environment may be followed by a space */
  1559.                if(!*lineptr){                    /* Permanent env change */
  1560.                   if(address==address1)address=address2; /* Swap buffers */
  1561.                   else address=address1;
  1562.                   if(i>=0)strcpy(address,varname); /* Copy given value */
  1563.                   break;
  1564.                }
  1565.                if(!i)break;     /* Error: No command follows "ADDRESS VALUE" */
  1566.                doaddress(&lineptr,varname);    /* Do the following command
  1567.                                                       in given environment */
  1568.                break;
  1569.             case PUSH: /* PUSH and QUEUE communicate with the stack.  The */
  1570.                        /* only difference between them is the command     */
  1571.                        /* letter: Q for QUEUE and S for PUSH.  We just    */
  1572.                        /* get the data to be stacked and write the        */
  1573.                        /* command, length and data down the socket.       */
  1574.                c='S';goto stack;
  1575.             case QUEUE:c='Q';
  1576.             stack: if(!*lineptr)len=0;
  1577.                else
  1578.               tmpchr=0,
  1579.               exp=scanning(lineptr,&tmpchr,&len),
  1580.           lineptr+=tmpchr;
  1581.                sprintf(pull,"%c%06X\n",c,len);
  1582.                if(write(rxstacksock,pull,8)<8||
  1583.                   (len>0&&write(rxstacksock,exp,len)<len)) die(Esys);
  1584.                break;
  1585.             /* Anything else is a syntax error.  However, under normal
  1586.             circumstances we should never get here. */
  1587.             default:die(Esyntax);
  1588.          }
  1589.          else{ /* The instruction starts with a printable character.  Try an
  1590.                assignment, and then a command to the environment. */
  1591.             varname[0]=0;
  1592.             if(rexxsymbol(c= *lineptr)==1){       /* the character is the */
  1593.                tmpchr=0,                          /* start of a symbol    */
  1594.                getvarname(lineptr,&tmpchr,varname,&l,maxvarname);
  1595.                if(lineptr[tmpchr]=='=')    /* it is an assignment if the  */
  1596.                   tmpchr++,                /* next character is '='       */
  1597.                   exp=scanning(lineptr,&tmpchr,&len),
  1598.           lineptr+=tmpchr,
  1599.                   varset(varname,l,exp,len);
  1600. /*             else if(curline[tmpchr]==EQU)die(Eassign); / * a == value */
  1601.                else varname[0]=0;
  1602.             } /* Next, if the character is not the start of a symbol, but is
  1603.             valid inside a symbol (i.e. a digit or dot), check to see whether
  1604.             it is an invalid assignment of the form 3=2+2 and reject if so. */
  1605. /*          else if(rexxsymboldot(c)){
  1606.                for(tmpchr=curchr;rexxsymboldot(curline[++tmpchr]););
  1607.                if(curline[tmpchr]=='=')die(Ename);
  1608.             } */
  1609.             /* Finally, if no assignment was found it must be a command */
  1610.             if(!varname[0]) doaddress(&lineptr,address);
  1611.          }
  1612. /* End of processing for each clause.  Now if chkend is set, we need to check
  1613.    for a clause terminator and step to the next statement. If chkend is not
  1614.    set, we are already pointing to the next clause. */
  1615.       doconds();  /* Test and carry out any signals */
  1616.       if(chkend){
  1617.          if(c= *lineptr)die(Edata);  /* if end-of-line not found, error */
  1618.          if(trcflag&Tclauses)printstmt(ppc,1,0); /* Trace intervening comments */
  1619.          ppc++;
  1620.       }
  1621.    }
  1622.    return anslen[0]=-1,cnull; /* End of program, so return */
  1623. }
  1624.  
  1625. static void doaddress(lineptr,env) /* The lineptr points to a command to be */
  1626. char **lineptr;                    /* executed in an environment */
  1627. char *env;
  1628. {
  1629.    char *cmd;
  1630.    char *cmdcopy;
  1631.    int len;
  1632.    char c;
  1633.    int i;
  1634.    if(trcflag&Tcommands)/* trace command before interpretation */
  1635.       printstmt(ppc,0,0);
  1636.    i=0;   
  1637.    cmdcopy=scanning(*lineptr,&i,&len); /* get a copy of the command for later*/
  1638.    rxdup();
  1639.    cmd=delete(&len);                   /* get the command */
  1640.    cmdcopy[len]=0;
  1641.    (*lineptr)+=i;
  1642.    if(   (trcflag&Tcommands)||  /* trace command before execution */
  1643.          (trcflag&~Tinteract)==(Tclauses|Tlabels)){
  1644.       if(prog[ppc].num)fprintf(traceout,"%5d *~* ",prog[ppc].num);
  1645.       else fputs("      *~* ",traceout);
  1646.       for(i=0;i<traceindent*pstacklev;i++)putc(' ',traceout);
  1647.       for(i=0;i<len;i++)putc((c=cmd[i]&127)<' '&&c<127? '?':c,traceout);
  1648.       putc('\n',traceout);
  1649.       interactive();
  1650.    }
  1651.    cmd[len]=0;
  1652.    /* Environment UNIX gives command to the Bourne shell; environment COMMAND
  1653.       gives it to the builtin shell.  For compatibility with Regina, SYSTEM
  1654.       and PATH are allowed as synonyms for these two names, respectively */
  1655.    i=Eerror;         /* initially, any non-zero return code is an "error" */
  1656.    if(!strcmp(env,"UNIX") || !strcmp(env,"SYSTEM")){
  1657.       c=system(cmd)/256;  /* for ADDRESS UNIX just call sh */
  1658.       if(c==1)i=Efailure; /* Unfortunately, failure produces RC 1 */
  1659.    }
  1660.    else if(!strcmp(env,"COMMAND") || !strcmp(env,"PATH"))
  1661.       c=shell(cmd);    /* For ADDRESS COMMAND call builtin shell */
  1662.    else c=-3;          /* For unknown environment return -3 */
  1663.    if(c<0)i=Efailure;  /* All negative return codes are failures */
  1664.    if(c==0)i=0;        /* All zero return codes are OK */
  1665.    if(   (i==Efailure&&(trcflag&Tfailures))||          /* Trace return code */
  1666.          (i&&(trcflag&(Tclauses|Terrors|Tcommands)))){
  1667.       if(!(trcflag&(Tcommands|Tclauses)))printstmt(ppc,0,0);
  1668.       printrc(c);
  1669.       interactive();
  1670.    }
  1671.    rcset((int)c,i,cmdcopy);  /* set RC unless this is an interactive command */
  1672. }
  1673.  
  1674. /* The arglist (each argument i of length arglen[i]) is parsed by the template
  1675.    written at line+ptr */
  1676. static void parse(arglist,arglen,up,line,ptr)
  1677. char *arglist[]; /* The list of strings to be parsed, ending with NULL */
  1678. int arglen[];    /* The lengths of all those strings */
  1679. int up;          /* whether UPPER was specified */
  1680. char *line;      /* The start of the line containing the parse template */
  1681. int *ptr;        /* The current character pointer positioned at the template */
  1682. {
  1683.    char *srch;        /* A string to search for */
  1684.    int srchlen;       /* The length of the search string */
  1685.    int i;             /* Which string is being parsed */
  1686.    int j;             /* The current position within the string (0-based) */
  1687.    int l;             /* The length of the string being parsed */
  1688.    int lastexpr;      /* The start position of the last expression */
  1689.    int startvar,lenvar; /* The position of a variable list */
  1690.    int k,m1,e1,z1,l1,pos;
  1691.    char c;
  1692.    for(i=0;;i++){     /* For each string, until the template finishes */
  1693.       if(arglist[i]==cnull) /* no strings left, so parse a null string */
  1694.          i--,l=0;
  1695.       else l=arglen[i]; /* l holds the string length */
  1696.       j=0;
  1697.       lastexpr=-1;
  1698.       while(1){
  1699.          if(line[*ptr]==' ')++*ptr; /* A space may separate the previous piece
  1700.                                    of template from the next */
  1701.          startvar=*ptr; /* collect space-separated list of symbols or dots */
  1702.          while(rexxsymbol(c=line[*ptr])==1
  1703.            || c=='.'&&!rexxsymboldot(line[*ptr+1])){
  1704.             if(c!='.')skipvarname(line,ptr);
  1705.             else (*ptr)++;
  1706.             if(line[*ptr]==' ')++*ptr;
  1707.          }
  1708.          lenvar=*ptr-startvar; /* we now have the list stored for later */
  1709.          if(c<=0||c==','){ /* parse rest of line */
  1710.             pset1(line+startvar,lenvar,arglist[i]+j,l-j,up);
  1711.             break;
  1712.          }
  1713.          if(c=='('){   /* parse expression */
  1714.             (*ptr)++,
  1715.             srch=scanning(line,ptr,&srchlen);
  1716.             if(line[(*ptr)++]!=')')die(Elpar);
  1717.          }
  1718.          else if(c=='\''||c=='\"'){  /* parse string literal */
  1719.             srch=line+ ++(*ptr);
  1720.             while(line[(*ptr)++]!=c||line[*ptr]==c)
  1721.                   if(line[*ptr-1]==c)(*ptr)++; /* search for close quote */
  1722.             srchlen= (*ptr+line)-srch-1;
  1723.         /* Stack the string, whether hex, binary or ordinary */
  1724.             if(line[*ptr]=='X'&&!rexxsymboldot(line[*ptr+1]))
  1725.            stackx(srch,srchlen),
  1726.                (*ptr)++;
  1727.             else if(line[*ptr]=='B'&&!rexxsymboldot(line[*ptr+1]))
  1728.            stackb(srch,srchlen),
  1729.                (*ptr)++;
  1730.             else stackq(srch,srchlen,c);
  1731.             srch=delete(&srchlen);
  1732.          }
  1733.          else { /* parse numeric. c holds the sign (+,-,=) if any. Stack the
  1734.         number; leave srchlen positive or else get the integer in pos and
  1735.         leave srchlen negative */
  1736.             if((c=='+'||c=='-'||c=='=')&&line[++*ptr]=='('){
  1737.                ++*ptr;
  1738.                scanning(line,ptr,&srchlen);
  1739.                if(line[(*ptr)++]!=')')die(Elpar);
  1740.                pos=getint(1);
  1741.                srchlen= -1;
  1742.             }
  1743.             else{
  1744.                for(k= *ptr;rexxsymboldot(line[*ptr]);(*ptr)++);
  1745.                if(k== *ptr)die(Eparse);
  1746.                stack(srch=line+k,srchlen= *ptr-k);
  1747.             }
  1748.             if(c=='='||c=='+'||c=='-'||num(&m1,&e1,&z1,&l1)>=0){
  1749.             /* A number has now been found.  It is used as an absolute
  1750.         position, or an offset from the last position, or from the
  1751.         *start* of the previous search string */
  1752.                if(srchlen>=0)pos=getint(1); /* now pos holds the number */
  1753.                k=lastexpr>=0?lastexpr:j;    /* k holds the old position */
  1754.                if(c=='+')j=k,k+=pos;
  1755.                else if(c=='-')j=k,k-=pos;
  1756.                else k=pos-1; /* Absolute positions are 1-based, so decrement */
  1757.                if(k<0)k=0; /* Make sure position is within the line */
  1758.                if(k>l)k=l;
  1759.         /* Now, j holds the old position (i.e. start position), and k holds
  1760.         the new (i.e. stop position). */
  1761.                if(k<=j) /* parse from j to end of line */
  1762.               pset1(line+startvar,lenvar,arglist[i]+j,l-j,up);
  1763.                else /* parse from j to k */
  1764.               pset1(line+startvar,lenvar,arglist[i]+j,k-j,up);
  1765.                j=k; /* In each case now move to the new position */
  1766.                lastexpr=-1; /* No previous search string */
  1767.                continue;
  1768.             }
  1769.             else die(Eparse); /* A non-numeric symbol was found */
  1770.          } /* Now, a search string has been found, and it is stored in
  1771.        srch, and has length srchlen. */
  1772.          if(srchlen==0) /* The null string matches the end of the line. */
  1773.             k=l;
  1774.          else for(k=j;k<=l-srchlen;k++){ /* Do the search */
  1775.             for(l1=0;l1<srchlen&&uc1(arglist[i][k+l1],up)==srch[l1];l1++);
  1776.             if(l1==srchlen)break;
  1777.          }
  1778.          if(k>l-srchlen)k=l; /* not found, so move to end of line */
  1779.          pset1(line+startvar,lenvar,arglist[i]+j,k-j,up);
  1780.          if(k==l)j=k,lastexpr=-1;
  1781.          else j=k+srchlen,lastexpr=k; /* Move to end of string, but save the */
  1782.       }                               /* start position */
  1783.       /* End of loop: continue round if a comma is found, otherwise break. */
  1784.       if(line[*ptr]!=',')break;
  1785.       (*ptr)++;
  1786.    }
  1787. }
  1788.  
  1789. static char uc1(c,up) /* Return the uppercase of c, only if up is true. */
  1790. char c;
  1791. int up;
  1792. {
  1793.    if(up)return uc(c);
  1794.    return c;
  1795. }
  1796.  
  1797. /* parse a value with a space-separated list of names */
  1798. static void pset1(list,listlen,val,len,up) 
  1799. char *list;   /* A pointer to the list of names */
  1800. int listlen;  /* The length of the list of names */
  1801. char *val;    /* A pointer to the value */
  1802. int len;      /* The length of the value */
  1803. int up;       /* Whether to uppercase the value */
  1804. {
  1805.    static char varname[maxvarname]; /* For storing variable names */
  1806.    int varlen;                      /* The length of a variable name */
  1807.    int ptr;
  1808.    if(!listlen)return; /* No names - nothing to do */
  1809.    while(listlen){
  1810.       varname[0]=varlen=ptr=0;
  1811.       if(list[0]!='.') /* Get the next name, unless we are at "." */
  1812.          getvarname(list,&ptr,varname,&varlen,maxvarname);
  1813.       else ptr++;
  1814.       if(list[ptr]==' ')ptr++;
  1815.       list+=ptr;       /* Step past the name just encountered */
  1816.       if(listlen-=ptr){ /* not end of name list: return first token stripped */
  1817.          while(len&&val[0]==' ')val++,len--;
  1818.          for(ptr=0;ptr<len&&val[ptr]!=' ';ptr++);
  1819.       }
  1820.       else ptr=len; /* return remains of string, unstripped */
  1821.       pset(varname,varlen,val,ptr,up);
  1822.       val+=ptr;
  1823.       if(len-=ptr)val++,len--;/* absorb one space if necessary */
  1824.    }
  1825. }
  1826.  
  1827. /* trace and assign a result from the parse command */
  1828. static void pset(varname,namelen,val,len,up)
  1829. char *varname; /* The name to assign to; varname[0]==0 if the name was "." */
  1830. int namelen;   /* The length of the name */
  1831. char *val;     /* The value to assign */
  1832. int len;       /* The length of the value */
  1833. int up;        /* Whether to uppercase */
  1834. {
  1835.    char *sp;                  /* Some work space */
  1836.    static char what[4]=">>>"; /* Trace message prefix */
  1837.    static char buff[255];     /* A fixed length workspace */
  1838.    int x;
  1839.    if(trcflag&(Tresults|Tintermed)){ /* Trace the result */
  1840.       what[1]=(varname[0]?'>':'.');
  1841.       if(!(up&&len))traceline(what,val,len);
  1842.       else{
  1843.          sp=allocm((unsigned)len);
  1844.          for(x=0;x<len;x++)sp[x]=uc(val[x]);
  1845.          traceline(what,sp,len);
  1846.          free(sp);
  1847.       }
  1848.    }
  1849.    if(varname[0]){    /* Assign, unless the name was "." */
  1850.       if(!(up&&len))  /* Straightforward, unless it needs to be uppercased */
  1851.          varset(varname,namelen,val,len);
  1852.       else{
  1853.          sp=(len<256?buff:allocm((unsigned)len));/* Make some space */
  1854.          for(x=0;x<len;x++)sp[x]=uc(val[x]);     /* Uppercase into the space */
  1855.          varset(varname,namelen,sp,len);         /* Assign the uppercase val */
  1856.          if(len>255)free(sp);                    /* Now free the space */
  1857.       }
  1858.    }
  1859. }
  1860.  
  1861. static int findsigl(level)/* Save the current program, go down stack to find */
  1862. int *level;        /* the most recent non-interpreted instruction, and       */
  1863. {                  /* store the proper program in "prog".  "level" gets the  */
  1864.                    /* interplev of this instruction.                         */
  1865.    int sigl;
  1866.    int instr;
  1867.    int type;
  1868.    int len;
  1869.    char *ptr;
  1870.    int eptr;
  1871.    int lev=interplev;
  1872.    oldprog=prog;
  1873.    oldstmts=stmts;
  1874.    sigl=prog[instr=ppc].num;
  1875.    for(ptr=pstackptr+(eptr=epstackptr);!sigl&&eptr;){
  1876.       type=*((int *)ptr-1);
  1877.       ptr-=(len= *((int *)ptr-2)); /* point to start of entry */
  1878.       eptr-=len;
  1879.       if(type==14)
  1880.          prog=((struct interpstack *)ptr)->prg,
  1881.          stmts=((struct interpstack *)ptr)->stmts,
  1882.      lev--;
  1883.       sigl=prog[instr=((struct minstack *)ptr)->stmt].num;
  1884.    }
  1885.    return *level=lev,instr;
  1886. }
  1887.  
  1888. /* This function deletes argc arguments from the current calculator
  1889.    stack and stores their addresses and lengths in the given arrays */
  1890. static void getcallargs(args,arglen,argc)
  1891. int argc;     /* How many */
  1892. char *args[]; /* Where to put the pointers */
  1893. int arglen[]; /* Where to put the lengths */
  1894. {
  1895.    int i;
  1896.    for(i=argc-1;i>=0;i--)args[i]=delete(&arglen[i]);
  1897.    args[argc]=cnull;
  1898. }
  1899.  
  1900. /* A `call' command interpreter. The integer result is 1 if the call
  1901.    returned a value (placed on the calculator stack), 0 otherwise. */
  1902. int rxcall(stmt,name,argc,lit,how)
  1903. int stmt;    /* Where to call if this is a condition trap */
  1904. char *name;  /* What to call (as given in the CALL instruction) */
  1905. int argc;    /* How many args were given (on the calculator stack) */
  1906. char *how;   /* this is either "FUNCTION" or "SUBROUTINE" */
  1907. int lit;     /* whether or not the name was a quoted literal (if it was,  */
  1908. {            /* lit=1 and the internal label table is not searched */
  1909.    char *lptr;             /* A label pointer */
  1910.    struct procstack *sptr; /* A program stack item pointer */
  1911.    int l;
  1912.    char *result;           /* The result returned by the subroutine */
  1913.    int rlen;               /* The length of the result */
  1914.    char *args[maxargs+1];  /* The arguments given by the CALL instruction */
  1915.    int arglen[maxargs];    /* The lengths of the arguments */
  1916.    int type=0;             /* The type of a program stack entry */
  1917.    void *dlhandle;         /* The handle of a dynamically loaded module */
  1918.    int (*dlfunc)();        /* The address of a function in same */
  1919.    dictionary *dldict;     /* The address of the dictionary in same */
  1920.    char file[maxvarname+5];/* The name of a program file to load */
  1921.    int ext=0;              /* Whether the subroutine is external or internal */
  1922.    funcinfo *data;         /* data about an already loaded function */
  1923.    char *callname;         /* By what name the subroutine was called */
  1924.    char **oldcarg=curargs; /* The saved parameters of the current program...*/
  1925.    int *oldcarglen=curarglen;
  1926.    char *oldcstackptr;
  1927.    char oldtrcres=trcresult;
  1928.    long oldsec=timestamp.tv_sec;
  1929.    long oldmic=timestamp.tv_usec;
  1930.    char *flname;           /* The file name to load */
  1931.    int newprecision=9;     /* external calls have this for NUMERIC DIGITS */
  1932.    int w;
  1933.    char c;
  1934.    int delay=0;
  1935.    int sigl=0;             /* line to come from */
  1936.  
  1937.    while(argc&&isnull())argc--,delete(&w);/* The last arg should not be null */
  1938.    if(argc>maxargs)die(Emanyargs); /* Too much to handle */
  1939.  
  1940.    if(!name){/* called as a condition trap, so no need to search for a label */
  1941.       delay=lit; /* as a parameter-saving device, the delayed signal was
  1942.                     passed as the "lit" parameter. */
  1943.       name=conditions[delay];    /* the real name has been lost.  Use the
  1944.                                     condition name. */
  1945.       if(delay==Ihalt)sigl=haltline;
  1946.    }
  1947.    else{        
  1948. /* check for internal label */
  1949.       if(!lit)
  1950.          for(lptr=labelptr;(l= *(int *)lptr)&&strcasecmp(name,lptr+2*four);
  1951.              lptr+=align(l+1)+2*four);
  1952.       if(l)stmt=((int*)lptr)[1];
  1953.       if(lit||!l){ /* no label, so try built-in and then external */
  1954.          if((l=rxfn(name,argc))>0)return 1; /* OK, builtin was executed */
  1955.          if(callname=strrchr(name,'/')) /* Get base name for "callname" */
  1956.             callname++;
  1957.          else callname=name;
  1958.          if(data=(funcinfo *)hashget(2,callname,&w)){ /* function is loaded */
  1959.             l=(data->dlfunc)(callname,argc);
  1960.         if(l<0)die(-l);
  1961.         return l;
  1962.          }
  1963.          if(l)flname=rxmath, /* if it's a mathfn, look in the rxmathfn file */
  1964.               newprecision=precision; /* also, allow digits() to work... */
  1965.          else{ /* Make the file name in lower case in the workspace */
  1966.             ext=strlen(name);
  1967.             mtest(workptr,worklen,ext+1,worklen-ext+1);
  1968.             for(l=0;c=name[l];l++)workptr[l]=c>='A'&&c<='Z'?name[l]|32:name[l];
  1969.         workptr[l]=0;
  1970.             flname=workptr;
  1971.          }
  1972.          if(!(w=which(flname,2,file)))       /* Search for the file, but... */
  1973.             sprintf(workptr,": \'%s\'",name), /* die if not found */
  1974.         errordata=workptr,
  1975.         die(Eundef);
  1976.          if(w==1){ /* The file is a Rexx program, so load it */
  1977.             ext=2;
  1978.             oldlabels=labelptr,
  1979.             oldlines=lines,
  1980.         oldstmts=stmts,
  1981.             oldsource=source,
  1982.             oldprog=prog,
  1983.             oldppc=ppc,
  1984.         ippc=ppc; /* for error reports during the load */
  1985.         if(!(result=load(file,&rlen)))
  1986.            errordata=file,die(-3);/* a system error prevented the load */
  1987.         strcpy(fname,file);       /* For die() in case an error occurs */
  1988.         totpstacklev++;           /* For die() in case an error occurs */
  1989.         tokenise(result,rlen,0,0);
  1990.         source[0]=allocm(strlen(file)+1);
  1991.         totpstacklev--;           /* Restore to normality */
  1992.         strcpy(source[0],file);
  1993.             ppc=oldppc;  /* Get back the old value, to be stacked */
  1994.          }
  1995.          else { /* executable function must be linked.  All functions from the
  1996.                    dictionary will be loaded and hashed.  Exactly one of these
  1997.            will have a non-null dlhandle entry. */
  1998.             if(!(dlhandle=dlopen(file,1)))
  1999.                fputs(dlerror(),stderr),fputc('\n',stderr),die(Esys);
  2000.             dlfunc=(int(*)())dlsym(dlhandle,"rxfunction");
  2001.             dldict=(dictionary *)dlsym(dlhandle,"rxdictionary");
  2002.             if(dlfunc)funcinit(callname,dlhandle,dlfunc),dlhandle=0;
  2003.         if(dldict)
  2004.            while(dldict->name){
  2005.               funcinit(dldict->name,dlhandle,dldict->function);
  2006.               dlhandle=0;
  2007.               if(!dlfunc&&!strcasecmp(dldict->name,callname))
  2008.                  dlfunc=dldict->function;  /* ...this is the required fn */
  2009.               dldict++;
  2010.            }
  2011.         if(!dlfunc) /* Function wasn't found in the file */
  2012.                sprintf(workptr,": \'%s\' in file %s",name,file),
  2013.            errordata=workptr,
  2014.            die(Eundef);
  2015.             l=dlfunc(callname,argc);  /* Call the required function. */
  2016.             if(l<0)die(-l);
  2017.             return l;
  2018.          }
  2019.       }
  2020.    }
  2021.    /* The subroutine is Rexx.  ext==0 and stmt is the statement to go to,
  2022.       or ext==2 and the new program has been loaded. */
  2023.    if(!ext){  /* now set SIGL as appropriate */
  2024.       l=findsigl(&rlen);
  2025.       l=prog[l].num;    /* get the "real" program and find line */
  2026.       if(!sigl)sigl=l;  /* Set SIGL unless it was already given by a "halt" */
  2027.       sprintf(file,"%d",sigl),
  2028.       varset("SIGL",4,file,strlen(file)); /* ("file" is unused in this case) */
  2029.    }
  2030.    getcallargs(args,arglen,argc),
  2031.    oldcstackptr=cstackptr,
  2032.    cstackptr=allocm(100);
  2033.    sptr=(struct procstack *) /* We now stack a program stack item... */
  2034.       pstack(11+ext,ext?sizeof(struct procstack):sizeof(struct procstack2));
  2035.    sptr->csp=oldcstackptr,
  2036.    sptr->ecsp=ecstackptr,
  2037.    sptr->csl=cstacklen,
  2038.    sptr->trc=trcflag,
  2039.    sptr->tim=timeflag,
  2040.    sptr->mic=microsecs,
  2041.    sptr->sec=secs,
  2042.    sptr->form=numform,
  2043.    sptr->digits=precision,
  2044.    sptr->fuzz=fuzz;
  2045.    sptr->stmts=oldstmts,
  2046.    sptr->prg=oldprog;
  2047.    if(ext)
  2048.       sptr->lines=oldlines,
  2049.       sptr->src=oldsource,
  2050.       sptr->lab=oldlabels,
  2051.       sptr->lev=pstacklev,
  2052.       pstacklev=0,
  2053.       fuzz=precision=newprecision,
  2054.       numform=0;
  2055.    cstacklen=100, /* We allocated the new stack earlier (can't think why...) */
  2056.    ecstackptr=0;  /* Clear the stack now */
  2057.    if(++interplev>=sigstacklen) /* We might need more space on the sgstack */
  2058.       if(!(sgstack=(struct sigstruct *)
  2059.          realloc((char *)sgstack,sizeof(struct sigstruct)*(sigstacklen+=10))))
  2060.          die(Emem);
  2061.    trcresult=0;
  2062.    if(ext)
  2063.       newlevel(), /* an implicit PROCEDURE instruction */
  2064.       result=interpreter(&rlen,1,callname,how,args,arglen,0,0);
  2065.    else result=interpreter(&rlen,stmt,name,how,args,arglen,1,delay);
  2066.    /* Now, clean up, reclaim all the new structures, delete the program stack
  2067.    entry, replace the old values of certain things, etc */
  2068.    trcresult=oldtrcres,
  2069.    interplev--,
  2070.    oldcstackptr=cstackptr,
  2071.    timestamp.tv_sec=oldsec,
  2072.    timestamp.tv_usec=oldmic;
  2073.    while(type<11||type>13) /* Clear up all entries until ours */
  2074.       type=unpstack(),sptr=(struct procstack *)delpstack();
  2075.    cstackptr=sptr->csp,
  2076.    ecstackptr=sptr->ecsp,
  2077.    cstacklen=sptr->csl,
  2078.    trcflag=sptr->trc,
  2079.    timeflag=(timeflag&4)|(sptr->tim & 3),
  2080.    microsecs=sptr->mic,
  2081.    secs=sptr->sec,
  2082.    numform=sptr->form,
  2083.    precision=sptr->digits,
  2084.    fuzz=sptr->fuzz;
  2085.    if(result)stack(result,rlen);
  2086.    free(oldcstackptr); /* Now the result has been used, free the old stack */
  2087.    if(ext)             /* Free the program which was just executed */
  2088.       free(source[0]),
  2089.       free(source[1]),
  2090.       free((char*)source),
  2091.       free(prog[0].line),
  2092.       free((char*)prog),
  2093.       free(labelptr),
  2094.       lines=sptr->lines,
  2095.       stmts=sptr->stmts,
  2096.       source=(sptr->src),
  2097.       prog=(sptr->prg),
  2098.       labelptr=sptr->lab,
  2099.       pstacklev=sptr->lev-1;
  2100.    else
  2101.       stmts=sptr->stmts,
  2102.       prog=(sptr->prg);
  2103.    if(type>11) /* reclaim procedural variables */
  2104.       varstkptr--;
  2105.    curargs=oldcarg,
  2106.    curarglen=oldcarglen;
  2107.    ppc=newppc;
  2108.    if(!ext&&rlen<0){  /* the program fell off the end, so EXIT */
  2109.       returnfree=0;
  2110.       returnval=0;
  2111.       while(pstacklev){
  2112.          type=unpstack();
  2113.          freestack(delpstack(),type);
  2114.       }
  2115.       _longjmp(sgstack[interplev].jmp,-1);
  2116.    }
  2117.    return result!=cnull;
  2118. }
  2119.  
  2120. /* A function to execute the `interpret' command.  The return is either null,
  2121. or a pointer to a result string which was given in a RETURN instruction. */
  2122. char *rxinterp(exp,len,rlen,name,how,args,arglen)
  2123. char *exp;        /* The string to be interpreted */
  2124. int len;          /* The length of the string */
  2125. int *rlen;        /* The length of a value returned, if any */
  2126. char *name,*how;  /* The name and method (i.e. FUNCTION or SUBROUTINE) */
  2127. char *args[];     /* The array of arguments to the current Rexx function */
  2128. int arglen[];     /* The array of lengths of arguments */
  2129. {
  2130.    void process();           /* The tokeniser used by load() */
  2131.    struct interpstack *sptr; /* A program stack item pointer */
  2132.    char *result;             /* The result to be returned, if any */
  2133.    int type=0;
  2134.    if(!len)return cnull;     /* interpret null string is OK immediately */
  2135.    result=allocm(len+1);
  2136.    memcpy(result,exp,len);
  2137.    result[len]='\n';
  2138. /* tokenise... */
  2139.    oldstmts=stmts;
  2140.    oldprog=prog;
  2141.    ippc=ppc;
  2142.    interpreting=1;
  2143.    tokenise(result,len+1,1,0);
  2144.    interpreting=0;
  2145.    ppc=ippc;
  2146. /* Fill in a program stack entry */
  2147.    sptr=(struct interpstack *)pstack(14,sizeof(struct interpstack));
  2148.    sptr->stmts=oldstmts;
  2149.    sptr->prg=oldprog;
  2150.    ecstackptr=0;
  2151.    if(++interplev>=sigstacklen)/* might need some more space for the sgstack*/
  2152.       if(!(sgstack=(struct sigstruct *)
  2153.          realloc((char *)sgstack,sizeof(struct sigstruct)*(sigstacklen+=10))))
  2154.          die(Emem);
  2155. /* This is where the string gets interpreted */
  2156.    result=interpreter(rlen,1,name,how,args,arglen,1,0);
  2157.    if((type=unpstack())!=14)die(Enoend); /* Report any incomplete DO/etc */
  2158.    interplev--;
  2159. /* Unstack the program stack entry and all preceding DO structures, etc */
  2160. /* while(type!=14)type=unpstack(),sptr=(struct interpstack *)delpstack();*/
  2161.    sptr=(struct interpstack *)delpstack(),
  2162.    ppc=newppc,
  2163.    free(prog[0].source),  /* the interpreted string */
  2164.    free(prog[0].line),    /* the tokenised string */
  2165.    free((char*)prog),     /* the statement table */
  2166.    stmts=((struct interpstack *)sptr)->stmts,
  2167.    prog=((struct interpstack *)sptr)->prg;
  2168.    return result;
  2169. }
  2170.  
  2171. static void doconds()   /* check for delayed conditions and trap them */
  2172. {
  2173.    int cond;
  2174.    struct errorstack *tmpptr;
  2175.    int len;
  2176.    for(cond=0;cond<Imax;cond++)
  2177.       if(delayed[cond]){
  2178.          if((sgstack[interplev].callon&(1<<cond)) &&
  2179.        !(sgstack[interplev].delay &(1<<cond))){
  2180.        delayed[cond]=0;
  2181.        if(sgstack[interplev].ppc[cond]<0){ /* report an undefined label */
  2182.           tmpptr=(struct errorstack *)pstack(20,sizeof(struct errorstack));
  2183.           tmpptr->prg=prog;
  2184.           tmpptr->stmts=stmts;
  2185.           ppc=-sgstack[interplev].ppc[cond];
  2186.           findsigl(&cond);
  2187.           errordata=0;
  2188.           die(Elabel);
  2189.        } /* now call the condition routine */
  2190.        if(rxcall(sgstack[interplev].ppc[cond],cnull,0,cond,"SUBROUTINE"))
  2191.           delete(&len);             /* Ignore the return value */
  2192.        cond--;                      /* check this signal again */
  2193.      }
  2194.      else if(cond!=Ihalt)delayed[cond]=0; /* Cancel delayed conditions */
  2195.       }
  2196.    /* check for interruption */     
  2197.    if(delayed[Ihalt] && !(sgstack[interplev].delay&(1<<Ihalt)))
  2198.       delayed[Ihalt]=0,die(Ehalt);
  2199. }
  2200.  
  2201. void settrace(option)   /* Sets the trace flag according to the given option */
  2202. char *option;
  2203. {
  2204.    char c;
  2205.    if(!*option){
  2206.       otrcflag=trcflag=Tfailures;
  2207.       return;
  2208.    }
  2209.    while((c=*option++)=='?')trcflag^=Tinteract;
  2210.    interactmsg=(trcflag&Tinteract);
  2211.    switch(c&0xdf){
  2212.       case 'A':c=Tclauses;               break;
  2213.       case 'C':c=Tcommands|Terrors;      break;
  2214.       case 'E':c=Terrors;                break;
  2215.       case 'F':c=Tfailures;              break;
  2216.       case 'I':c=Tclauses|Tintermed;     break;
  2217.       case 'L':c=Tlabels;                break;
  2218.       case 'N':c=Tfailures;              break;
  2219.       case 'O':c=trcflag=interactmsg=0;  break;
  2220.       case 'R':c=Tclauses|Tresults;      break;
  2221.       case 0:                            break;
  2222.       default:die(Etrace);
  2223.    }
  2224.    otrcflag=trcflag=(trcflag&Tinteract)|c;
  2225. }
  2226.  
  2227. static int setoption(option,len) /* Interpret an option from the OPTIONS */
  2228. char *option;                    /* instruction or a commandline parameter. */
  2229. int len;                         /* Return 1 if the option was processed */
  2230. {                                /* This routine does not raise errors. */
  2231.    static char buffer[maxvarname];
  2232.    char *ptr=memchr(option,'=',len);
  2233.    FILE *fp;
  2234.    int equals=ptr?ptr-option:0;
  2235.    if(len>=maxvarname)return 0;
  2236.    if(equals>=5 && !strncasecmp(option,"tracefile",equals)){
  2237.       option+= ++equals;
  2238.       len-=equals;
  2239.       if(!len || memchr(option,0,len))return 0;
  2240.       if(option[0]=='\'' || option[0]=='\"'){
  2241.          if(option[len-1]!=option[0])return 0;
  2242.      option++;
  2243.      len-=2;
  2244.       }
  2245.       memcpy(buffer,option,len);
  2246.       buffer[len]=0;
  2247.       if(!(fp=fopen(buffer,"a")))perror(buffer);
  2248.       else {
  2249.          if(traceout!=stderr)fclose(traceout);
  2250.          traceout=fp;
  2251.      printf("Writing trace output to %s\n",buffer);
  2252.       }
  2253.       return 1;
  2254.    }
  2255.    return 0;
  2256. }
  2257.  
  2258. static int gettrap(lineptr,on,stmt)/* Get a trap name after "call/signal on" */
  2259. char **lineptr;        /* pointer to the trap name */
  2260. int on;                /* whether "on" or "off" */
  2261. int *stmt;             /* the statement number to go to on error */
  2262. {                      /* Return the trap number */
  2263.    int l;
  2264.    int i;
  2265.    int *lptr;
  2266.    int tmpchr=1;
  2267.    gettoken(*lineptr,&tmpchr,varnamebuf,varnamelen,0);
  2268.    lineptr[0]+=tmpchr;
  2269.    for(i=0;i<Imax && strcasecmp(varnamebuf,conditions[i]);i++);
  2270.    if(i==Imax)die(Etrap);
  2271.    if(on && **lineptr==NAME)
  2272.       tmpchr=1,
  2273.       gettoken(*lineptr,&tmpchr,varnamebuf,varnamelen,0),
  2274.       lineptr[0]+=tmpchr;
  2275.    /* varnamebuf now holds the name to go to on error */
  2276.    if(on){
  2277.       for(lptr=(int *)labelptr;
  2278.       (l= *lptr)&&strcasecmp(varnamebuf,(char *)(lptr+2));
  2279.       lptr+=2+align(l+1)/four);
  2280.       if(l)l=lptr[1]; /* l holds the stmt to go to on error */
  2281.    }
  2282.    *stmt=l;
  2283.    return i;
  2284. }
  2285.  
  2286. static void testvarname(lineptr,var,len)/* Check that any symbol in the   */
  2287. char **lineptr;                  /* current line, pointed to by lineptr,  */
  2288. char *var;                       /* matches the stored control variable   */
  2289. int len;                         /* name, var, of length len.             */
  2290. {
  2291.    char c;
  2292.    char *varref;
  2293.    int reflen;
  2294.    if (c= **lineptr){                     /* if the symbol name is supplied: */
  2295.       if (c<0)die(Exend);                 /* die if it is a keyword [SELECT] */
  2296.       if(rexxsymbol(c)<1)die(Enosymbol);  /* or an invalid symbol            */
  2297.       varref= *lineptr;                   /* Save start addr of symbol       */
  2298.       reflen=0;
  2299.       skipvarname(*lineptr,&reflen);      /* go to end of symbol             */
  2300.       if(len!=reflen||memcmp(varref,var,len))
  2301.          die(Exend);                      /* die if it is the wrong symbol   */
  2302.       lineptr[0]+=reflen;
  2303.    }
  2304. }
  2305.  
  2306. static void skipstmt(){ /* Skips the current instruction */
  2307.    switch(prog[ppc].line[0]){  /* Test for block instructions */
  2308.       case DO:    stepdo();     return; 
  2309.       case SELECT:stepselect(); return;
  2310.       case IF:    stepif();     return;
  2311.       case WHEN:  stepwhen();   return;
  2312.       default:                 /* Skip one statement */
  2313.          if(++ppc==stmts)die(Enoend);
  2314.      return;
  2315.    }
  2316. }
  2317. /* The following functions, stepdo(), stepselect(), stepif() and stepwhen(),
  2318.    do the work of skipstmt() in the special cases of DO, SELECT and IF
  2319.    instructions. */
  2320. static void stepdo()
  2321. {
  2322.    pstack(0,sizeof(struct minstack));
  2323.               /* in case of error, report loop start as well as end */
  2324.    if(++ppc==stmts)die(Enoend);             /* go past DO */
  2325.    while(prog[ppc].line[0]!=END)skipstmt(); /* find END */
  2326.    if(prog[ppc].line[1]<0)die(Exend);       /* report error for "END SELECT" */
  2327.    delpstack();
  2328.    if(++ppc==stmts)die(Enoend);             /* go past END */
  2329. }
  2330. static void stepselect()
  2331. {
  2332.    char c;
  2333.    pstack(0,sizeof(struct minstack));
  2334.    if(++ppc==stmts)die(Enoend);             /* go past SELECT */
  2335.    while(prog[ppc].line[0]!=END)skipstmt(); /* find END */
  2336.    if((c=prog[ppc].line[1])&&c!=SELECT)die(Exend);/* report error for "END x"*/
  2337.    delpstack();
  2338.    if(++ppc==stmts)die(Enoend);             /* go past END */
  2339. }
  2340. static void stepif(){
  2341.    if(++ppc==stmts)die(Enoend);             /* go past IF */
  2342.    if(prog[ppc].line[0]!=THEN)die(Enothen); /* find THEN */
  2343.    if(++ppc==stmts)die(Enoend);             /* go past THEN */
  2344.    skipstmt();                              /* skip the statement after THEN */
  2345.    if(prog[ppc].line[0]==ELSE){             /* an ELSE clause is optional    */
  2346.       if(++ppc==stmts)die(Enoend);          /* go past ELSE */
  2347.       skipstmt();                           /* skip the statement after ELSE */
  2348.    }
  2349. }
  2350. static void stepwhen(){
  2351.    if(++ppc==stmts)die(Enoend);             /* go past WHEN */
  2352.    if(prog[ppc].line[0]!=THEN)die(Enothen); /* find THEN */
  2353.    if(++ppc==stmts)die(Enoend);             /* go past THEN */
  2354.    skipstmt();                              /* skip the statement after THEN */
  2355. }
  2356. static void findend(){ /* This function is called inside a SELECT, LEAVE or */
  2357.                       /* ITERATE to find the closing END statement.         */
  2358.    while(prog[ppc].line[0]!=END)skipstmt();
  2359. }
  2360.  
  2361. /* Each halt signal (SIGINT, SIGHUP, SIGTERM) is handled by recording it.    */
  2362. /* SIGHUP and SIGTERM are more forceful signals; too many of them terminates */
  2363. /* the interpreter.                                                          */
  2364. static void halt_handler(sig, code, scp, addr)
  2365. int sig, code;
  2366. struct sigcontext *scp;
  2367. char *addr;
  2368. {
  2369.    int dummy;
  2370.    int errstmt;
  2371. #ifdef SYSV
  2372.    signal(sig,halt_handler);
  2373. #endif
  2374.    errstmt=findsigl(&dummy);
  2375.    haltline=prog[errstmt].num;/* Find the line number at which halt occurred */
  2376.    prog=oldprog,stmts=oldstmts;
  2377.    delayed[Ihalt]++;
  2378.    switch(sig){
  2379.       case SIGINT: sigdata[Ihalt]="SIGINT"; putc('\n',ttyout); break;
  2380.       case SIGHUP: sigdata[Ihalt]="SIGHUP"; break;
  2381.       default:     sigdata[Ihalt]="SIGTERM";
  2382.    }
  2383.    if(sig!=SIGINT && delayed[Ihalt]>2)
  2384.       puts("Emergency stop"),
  2385.       killself(sig);
  2386. }
  2387.  
  2388. /* SIGSEGV, SIGBUS, SIGILL and SIGPIPE cause the interpreter to die, after */
  2389. /* killing the stack.                                                      */
  2390. static void error_handler(sig, code, scp, addr)
  2391. int sig, code;
  2392. struct sigcontext *scp;
  2393. char *addr;
  2394. {
  2395.    killself(sig);
  2396. }
  2397.  
  2398. static void sigtrace() /* A SIGQUIT is handled by going to interactive trace */
  2399. {                      /* mode, or by stopping immediately.  Only stop if we */
  2400.                        /* have already tried to interrupt the program.       */
  2401.    fputs("\b\b  \b\b",ttyout);
  2402.    fflush(ttyout);
  2403.    if(delayed[Ihalt] && (trcflag&Tinteract)){
  2404.       puts("Emergency stop");
  2405.       if(rxstackproc)kill(rxstackproc,SIGTERM);
  2406.       exit(1);
  2407.    }
  2408.    trcflag=Tinteract|Tclauses|Tlabels|Tresults;
  2409.    interactmsg=1;
  2410. }
  2411. static int killself(sig)/* This way, the shell prints a "terminated" message */
  2412. int sig;
  2413. {
  2414.    sigset_t sigset;     /* A set which will consist of the given signal */
  2415.    sigemptyset(&sigset);
  2416.    sigaddset(&sigset,sig);
  2417.    if(rxstackproc)kill(rxstackproc,SIGTERM);
  2418.    signal(sig,SIG_DFL);
  2419.    sigprocmask(SIG_UNBLOCK,&sigset,(sigset_t *)0); /* unblock the signal */
  2420.    if(kill(getpid(),sig))exit(1);
  2421.    pause();
  2422. }
  2423.